常见链表操作
以下所有的链表操作都基于单链表类。
并且假设未知链表的长度,没有取链表任意节点的指针的辅助函数(也就是不能用currentLength和move())
1.单链表翻转:就地翻转法,头节点插入法
头节点插入法:实现思路简单,把链表的数据从都头到尾读入到一个栈中,再依次出栈构成新的链表。头节点插入法适用于根据当前链表创建一个翻转的新链表,时间复杂度O(n),空间复杂度O(n)
就地翻转法:举例说明
head->1->2->3->4->5
head->2->1->3->4->5
head->3->2->1->4->5
head->4->3->2->1->5
head->5->4->3->2->1
思路就是从第二个节点开始,依次把每一个节点放到前方已经部分逆序的节点的头部。就地翻转法适用于直接改变当前的链表。时间复杂度O(n),空间复杂度O(1)
template<typename elemType> void sLinkList<elemType>::reverse(){ if (currentLength > 1){ node* p = head->next; node* q = p->next; while(q != NULL){ p->next = q->next; q->next = head->next; head->next = q; q = p->next; } } }
2.两个有序链表合并:思路很简单,分别用两个指针指向两个有序链表的表头,依次向下比较,最后把其中一个没有被遍历完的连表的所有节点都添加到新链表后。
template<typename elemType> sLinkList<elemType> sLinkList<elemType>::merge(const sLinkList<elemType> & s1, const sLinkList<elemType> & s2){ sLinkList<elemType> a; node* p0 = a.head; node* p1 = s1.head->next; node* p2 = s2.head->next; while(p1 != NULL && p2 != NULL){ if(p1->data < p2->data){ p0->next = new node(p1->data); p1 = p1->next; } else{ p0->next = new node(p2->data); p2 = p2->next; } p0 = p0->next; } while(p1 != NULL){ p0->next = new node(p1->data); p1 = p1->next; p0 = p0->next; } while(p2 != NULL){ p0->next = new node(p2->data); p2 = p2->next; p0 = p0->next; } a.currentLength = s1.currentLength + s2.currentLength; return a; }
3.链表环的检测:快慢指针法,足迹法
如果单链表中存在环,从任何一个节点开始依次访问的指针都一定会进入循环中,即一定会有节点被重复访问。散列表法即每遍历到一个节点都在散列表中查找该节点是否已被加入到散列表中,如果否则把节点加入散列表,如果是则说明存在环,遍历直到指针为NULL为止。
而快慢指针法更加简单,快指针和慢指针一开始都指向头节点,快指针一次走两个节点,慢指针一次走一个节点,如果存在环,必会在某次循环中两个指针相等。
template<typename elemType> bool sLinkList<elemType>::DetectRing(){ node *p = head, *q = head; while(q->next != NULL && q->next->next != NULL){ p = p->next; q = q->next->next; if(p == q) return true; } return false; }
4.删除链表倒数第k个节点:快慢指针法,散列表法
散列表法和上面思路类似,快慢指针法是先让快指针移动k个节点,然后再进入循环两个指针同步向后移动,直到快指针到达循环的边界。慢指针就是倒数第k个节点。
当然也可以先遍历一遍链表统计出节点数,然后在取第n-k+1个节点。而且这个方法理论上跟快慢指针法的效率是一样的。
template<typename elemType> elemType sLinkList<elemType>::FindKthFromBottom(int k){ node *p = head, *q = head; for(int i = 0; i < k; ++i) q = q->next; while(q != NULL){ p = p->next; q = q->next; } return p->data; }
5.求链表的中间节点:快慢指针法,散列表法
快慢指针法就是两个指向头节点的指针,一个一次走一个节点,一个一次走两个节点,当快指针走到尾时慢指针就刚好在中间。当然这个方法跟直接遍历一遍获取链表长度的效率也是一样的。
template<typename elemType> elemType sLinkList<elemType>::FindMiddle(){ if(head->next == NULL) throw NoSuchNodeError(); node* p1 = head; node* p2 = head; while(true){ if(p2->next == NULL) return (p1->data + p1->next->data) / 2; if(p2->next->next == NULL) return p1->next->data; p1 = p1->next; p2 = p2->next->next; } }
完整代码如下,可以直接测试
#include <iostream> using namespace std; // 线性表:顺序表和链表共同的抽象类 template<typename elemType> class list{ public: virtual void clear() = 0; // 删除线性表中的所有数据元素 virtual int length() const = 0; // 求线性表的长度 virtual void insert(int i, const elemType & x) = 0; // 在第i个位置插入一个元素 virtual void remove(int i) = 0; // 删除第i个位置的元素 virtual int search(const elemType & x) const = 0; // 搜索某个元素x在线性表中是否出现 virtual elemType visit(int i) const = 0; // 返回线性表中第i个数据元素的值 virtual void traverse() const = 0; // 按序访问线性表的每一数据元素 virtual ~list(){}; }; // 单链表 template<typename elemType> class sLinkList:public list<elemType>{ public: struct node{ // 单链表中的节点类 elemType data; node* next; node(const elemType & x, node* n = NULL) {data = x; next = n;} node():next(NULL){} ~node(){} }; node* head; // 头指针 int currentLength; // 表长 node* move(int i) const; // 返回指向第i个结点的指针 class NoSuchNodeError{}; // 按索引的结点不存在导致的错误 public: sLinkList(); ~sLinkList(){clear(); delete head;} void clear(); int length() const{return currentLength;} void insert(int i, const elemType & x); void remove(int i); int search(const elemType & x) const; elemType visit(int i) const; void traverse() const; void reverse(); static sLinkList<elemType> merge(const sLinkList<elemType> & s1, const sLinkList<elemType> & s2); elemType FindMiddle(); elemType FindKthFromBottom(int k); bool DetectRing(); }; template<typename elemType> typename sLinkList<elemType>::node* sLinkList<elemType>::move(int i) const{ node* p = head; while (i-- >= 0) p = p->next; return p; } template<typename elemType> sLinkList<elemType>::sLinkList(){ head = new node; // 哨兵结点 currentLength = 0; } template<typename elemType> void sLinkList<elemType>::clear(){ node* p = head->next; node* q; head->next = NULL; while(p != NULL){ q = p->next; delete p; p = q; } currentLength = 0; } template<typename elemType> void sLinkList<elemType>::insert(int i, const elemType & x){ if(i > currentLength || i < 0) throw NoSuchNodeError(); node* pos = move(i - 1); pos->next = new node(x, pos->next); ++ currentLength; } template<typename elemType> void sLinkList<elemType>::remove(int i){ if(i >= currentLength || i < 0) throw NoSuchNodeError(); node* pos = move(i - 1); node* delp = pos->next; pos->next = delp->next; delete delp; --currentLength; } template<typename elemType> int sLinkList<elemType>::search(const elemType & x) const{ node* p = head->next; int i = 0; while(p!= NULL && p->data != x){p = p->next; ++i;} if(p == NULL) return -1; else return i; } template<typename elemType> elemType sLinkList<elemType>::visit(int i) const{ if(i >= currentLength || i < 0) throw NoSuchNodeError(); return move(i)->data; } template<typename elemType> void sLinkList<elemType>::traverse() const{ node* p = head->next; while(p != NULL){ cout << p->data << " "; p = p->next; } cout << endl; } template<typename elemType> void sLinkList<elemType>::reverse(){ if (currentLength > 1){ node* p = head->next; node* q = p->next; while(q != NULL){ p->next = q->next; q->next = head->next; head->next = q; q = p->next; } } } template<typename elemType> sLinkList<elemType> sLinkList<elemType>::merge(const sLinkList<elemType> & s1, const sLinkList<elemType> & s2){ sLinkList<elemType> a; node* p0 = a.head; node* p1 = s1.head->next; node* p2 = s2.head->next; while(p1 != NULL && p2 != NULL){ if(p1->data < p2->data){ p0->next = new node(p1->data); p1 = p1->next; } else{ p0->next = new node(p2->data); p2 = p2->next; } p0 = p0->next; } while(p1 != NULL){ p0->next = new node(p1->data); p1 = p1->next; p0 = p0->next; } while(p2 != NULL){ p0->next = new node(p2->data); p2 = p2->next; p0 = p0->next; } a.currentLength = s1.currentLength + s2.currentLength; return a; } template<typename elemType> elemType sLinkList<elemType>::FindMiddle(){ if(head->next == NULL) throw NoSuchNodeError(); node* p1 = head; node* p2 = head; while(true){ if(p2->next == NULL) return (p1->data + p1->next->data) / 2; if(p2->next->next == NULL) return p1->next->data; p1 = p1->next; p2 = p2->next->next; } } template<typename elemType> elemType sLinkList<elemType>::FindKthFromBottom(int k){ node *p = head, *q = head; for(int i = 0; i < k; ++i) q = q->next; while(q != NULL){ p = p->next; q = q->next; } return p->data; } template<typename elemType> bool sLinkList<elemType>::DetectRing(){ node *p = head, *q = head; while(q->next != NULL && q->next->next != NULL){ p = p->next; q = q->next->next; if(p == q) return true; } return false; }