当有那么一种需求,就是需要两个栈存储,一个增加,另一个就减少时,可以考虑使用两栈共享空间。
就是一个数据域(数组表示),两个下标来标识两个的栈顶,当标识重叠时,就是栈满了。
栈其实是一个受限的线性表,只允许后进先出的概念,也就是出栈和入栈。
顺序存储结构也就是用一个数组来存储栈中的数据,数组实际数据域的末尾保存栈顶数据,用一个栈顶指标top保存这个末尾的下标。
出栈时,获取到当前top下标的数据,将top往前指。入栈时,将数据存储到目前栈顶top的下一位,然后将top往后指。
当然的,就是这个是用数组存储的,所以需要注意栈的大小。
所谓的双向链表就是可以通过任意一结点获取到它的前驱和后继。普通的单节点只有一个next的引用,用来指向下一个元素,而找不到它的上一个元素。而双向链表就是结点不一样,加多了一个pre的引用,用来指向上一个元素。
所以,在插入和添加等操作的时候,赋值next指针的同时,也需要多维护一下pre指针。
循环链表和普通链表差不多,就是循环链表的最后一个结点的下一个结点引用不为空了,而是为头结点。这样就可以通过遍历到的尾结点又回到头结点处。
它的好处就是可以通过任一结点,就可以完成链表的遍历了。
静态链表就是使用数组的方式来实现线性表的链式结构。它存储元素的个数是受限的。
主要用于没有指针引用等概念的编程语言。
思路就是有一个节点类型,是结构类型的值类型,保存数据和下一个元素的数组下标。数据域就是一个结点数组,比存储的数据大小多出两个节点来,第一个结点和最后一个节点,分别作为两条链表,一条用来保存没有用过的结点,一条用来保存数据结点,头末结点就是这两条链表的头结点,当需要添加元素,就从备用链表中获取到空闲结点,放置到数据链表中。删除时同样如此,将数据结点移除,放置到备用链表中。用下标0表示链表的结束。
//通过索引删除元素 public T DeleteByIndex(int index) { Node go = this.GetNodeByIndex(index - 1); if (go == null || go.next == null) { Console.WriteLine("索引非法!"); return default(T); } Node p = go.next; T old = p.data; go.next = p.next; p = null; if (index == this._curentLinkLength) { //说明删除的是最后一个元素 this._last = go; } this._curentLinkLength--; return old; }
同样的,删除指定位置的元素也是需要获取到这个位置的前一个元素的,同时还要获取到后一个元素,可以通过前一个元素的next的next获取到,然后直接修改前一个元素的next为后一个元素,那么这个要删除的位置的元素的引用就没有了,也就是凭空消失了。
如果有尾结点引用,需要注意的是删除最后一个结点的情况,需要修改尾结点的引用为前一个元素。
//向链表索引i前插入元素 public bool Insert(int index, T element) { Node go = this.GetNodeByIndex(index - 1); if (go == null || go.next == null) { Console.WriteLine("索引非法!"); return false; } Node add = new Node(); add.data = element; add.next = go.next; go.next = add; this._curentLinkLength++; return true; }
要在指定位置前插入元素,那么就要得到插入元素的前一个元素,这样就可以设置新加元素的下一个元素为index位置结点(就是pre结点的next结点),然后设置pre的next结点为新的结点就行了。
//向链表头部增加元素 public bool AddToHead(T element) { Node add = new Node(); add.data = element; add.next = _head.next; if (_head.next == null) { //判断是否是空链表,如果是,尾结点就是插入结点 this._last = add; } _head.next = add; this._curentLinkLength++; return true; }
在链表前面添加元素,这时候头结点的作用就来了,设置新结点的下一个结点为头结点的下一个结点(当前的第一个节点),修改头结点的下一个结点(第一个结点)为新结点。
注意如果有尾结点时,注意空链表时往链表前面添加元素的情况。
//向链表后面快速添加元素 public bool AddQuick(T element) { Node add = new Node(); add.data = element; add.next = null; this._last.next = add; this._last = add; this._curentLinkLength++; return true; }
直接使用尾部结点的引用,快速赋值next结点为新结点,再改变尾部结点为新结点。
如果没有尾部结点,则需要找到这个结点,通过遍历整个链表,直到结点的next引用为空,那么这个结点就是尾结点。
//获取指定位置的节点 private Node GetNodeByIndex(int index) { Node go = this._head; int i = 0; while (go != null && i < index) { go = go.next; i++; } if(i != index) go = null; return go; }
因为是链式结构,不能像数组那样直接下标获取元素了,只能遍历整个链表,一个一个元素往下,知道指定的位置。