顺序表适用于尾插尾删较多的场景;链表适用于头插或中间插入较多的场景。
递归方法的思想就是:先打印出cur结点之后的链表,再打印cur。
void PrintEndToStartR(pList plist) { pNode cur = plist; if (plist == NULL) return; if (cur->_next != NULL) { PrintEndToStartR(cur->_next); } printf("%d ", cur->_data); }非递归方法:借用栈,先把单链表的节点全部压入进栈,再依次取栈顶节点即可。
void PrintEndToStart() { stack<DataType> s; Node *cur = _head; while (cur) { s.push(cur->_data); cur = cur->_next; } while (!s.empty()) { cout << s.top() << " "; s.pop(); } cout << endl; }这实际是一种偷梁换柱的思想:把要删除的结点后一个结点的数据赋给删除结点,删除要删除结点后一个结点。是不是有点绕?看图吧!
void EraseWithoutHead(pNode pos) { assert(pos); pNode del = NULL; if (pos->_next) { del = pos->_next; pos->_data = del->_data; pos->_next = del->_next; free(del); } }思想:新建一个要插入数据的结点,把pos结点与新结点中的数据交换,再把新结点插到pos结点后。
void InsertWitnoutHead(pNode pos, int d) { assert(pos); pNode newNode = (pNode)malloc(sizeof(pNode)); newNode->_data = pos->_data; pos->_data = d; newNode->_next = pos->_next; pos->_next = newNode; }约瑟夫环问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。 注意释放结点之前要先把释放的那个结点之前的结点和之后的结点连接起来!
pNode JosephCycle(pList *pplist, int num) { assert(pplist); pNode cur = *pplist; pNode del = *pplist; int i = 0; while (cur->_next != cur) { for (i = 0; i < num - 1; i++) { cur = cur->_next; } printf("%d ", cur->_data); del = cur->_next; cur->_data = del->_data; cur->_next = del->_next; free(del); del = NULL; } return cur; }直接运用冒泡排序的思想。
void BubbleSortList(pList *pplist, size_t length) { pNode cur = *pplist; pNode next = NULL; DataType tmp = 0; if (*pplist == NULL || (*pplist)->_next == NULL) return; for (int i = 0; i < length; i++) { //每次循环都从链表的第一个节点开始 cur = *pplist; next = cur->_next; for (int j = 0; j < length - i - 1; j++) { if (cur->_data > next->_data) { tmp = cur->_data; cur->_data = next->_data; next->_data = tmp; } cur = cur->_next; next = cur->_next; } } }快慢指针法:快指针每次走两步,慢指针每次走一步,快指针走到链表最后一个结点时,慢指针指向中间节点
pNode FindMidNode(pList plist) { assert(plist); pNode fast = plist; pNode slow = plist; while (fast && fast->_next) { fast = fast->_next->_next; slow = slow->_next; } return slow; }快慢指针法:快指针先走k步,然后快慢指针再一起走,当快指针走到尾时,慢指针指向倒数第k个结点
pNode FindLastK(pList plist, int k) { assert(k > 0); pNode fast = plist; pNode slow = plist; int i = 0; for (i = k - 1; i > 0; i--) { if (fast && fast->_next) fast = fast->_next; } while (fast->_next) { fast = fast->_next; slow = slow->_next; } return slow; }判断是否带环 快慢指针法:快指针每次走两步,慢指针每次走一步,若快慢指针可以相遇则带环,返回相遇点,否则不带。 求环的长度 从相遇点开始遍历环,回到相遇点时即得到环的长度。 求环的入口点
//判断链表是否带环 pNode CheckCycle(pList plist)//O(N)=N { assert(plist); pNode fast = plist; pNode slow = plist; while (fast && fast->_next) { fast = fast->_next->_next; slow = slow->_next; if (fast == slow) return slow; } return NULL; } //O(N)=N int GetCyleLength(pNode meet) { pNode cur = meet; int count = 0; do { count++; cur = cur->_next; } while (cur != meet); return count; } //O(N)=N pNode GetCycleEntryNode(pList plist, pNode meetNode) { pNode head = plist; pNode meet = meetNode; while (head != meet) { head = head->_next; meet = meet->_next; } return head; }若两个链表相交,则它们的尾结点一定相等
//判断两个链表是否相交(假设链表不带环) //O(N)=N bool CheckCross(pList plist1, pList plist2) { pNode p1 = plist1; pNode p2 = plist2; if (plist1 == NULL || plist2 == NULL) return false; while (p1->_next) { p1 = p1->_next; } while (p2->_next) { p2 = p2->_next; } if (p1 == p2) return true; else return false; }求交点:先遍历两条链表,求出它们的长度差为k,两个指针分别从长链表的第k个节点和短链表的第一个节点开始同时走,当两个指针第一次指向的节点相同时,则此节点为交点。 当然也可调用前面的函数来求得交点。
//求交点(假设链表不带环) //O(N)=N pNode GetCrossNode1(pList plist1, pList plist2) { pNode p1 = plist1; pNode p2 = plist2; int length1 = 1; int length2 = 1; int i = 0; while (p1->_next) { p1 = p1->_next; length1++; } while (p2->_next) { p2 = p2->_next; length2++; } if (length1 > length2) { for (i = length1 - length2; i > 0; i--) { plist1 = plist1->_next; } } else { for (i = length2 - length1; i > 0; i--) { plist2 = plist2->_next; } } while (plist1 != plist2) { plist1 = plist1->_next; plist2 = plist2->_next; } return plist1; } pNode GetCrossNode2(pList plist1, pList plist2) { pNode p1 = plist1; pNode meetNode = NULL; pNode crossNode = NULL; while (plist1->_next) plist1 = plist1->_next; plist1->_next = plist2; meetNode = CheckCycle(p1); crossNode = GetCycleEntryNode(p1, meetNode); return crossNode; }不带环的情况不再分析; 一条链表带环,一条链表不带环则一定不相交; 带环的情况如下图所示:
//判断两个链表是否相交(假设链表可能带环) bool CheckCrossC(pList plist1, pList plist2) { pNode EntryNode1 = NULL; pNode EntryNode2 = NULL; pNode p1 = NULL; if (plist1 == NULL || plist2 == NULL) return false; if (CheckCycle(plist1) == NULL && CheckCycle(plist2) == NULL) return CheckCross(plist1, plist2); else if ((CheckCycle(plist1) != NULL && CheckCycle(plist2) == NULL) || \ (CheckCycle(plist1) == NULL && CheckCycle(plist2) != NULL)) return false; else { if (GetCycleEntryNode(plist1, CheckCycle(plist1)) == GetCycleEntryNode(plist2, CheckCycle(plist2))) return true; else { EntryNode1 = GetCycleEntryNode(plist1, CheckCycle(plist1)); p1 = EntryNode1; EntryNode2 = GetCycleEntryNode(plist2, CheckCycle(plist2)); do{ if (p1 == EntryNode2) return true; p1 = p1->_next; } while (p1 != EntryNode1); return false; } } } //求交点(假设链表可能带环) pNode GetCrossNodeC(pList plist1, pList plist2) { pNode EntryNode1 = NULL; pNode EntryNode2 = NULL; pNode p1 = NULL; pNode p2 = NULL; int length1 = 1; int length2 = 1; int i = 0; if (CheckCycle(plist1) == NULL && CheckCycle(plist2) == NULL) return GetCrossNode1(plist1, plist2); EntryNode1 = GetCycleEntryNode(plist1, CheckCycle(plist1)); EntryNode2 = GetCycleEntryNode(plist2, CheckCycle(plist2)); p1 = plist1; p2 = plist2; if (EntryNode1 == EntryNode2) { while (p1->_next != EntryNode1) { p1 = p1->_next; length1++; } while (p2->_next != EntryNode1) { p2 = p2->_next; length2++; } if (length1 > length2) { for (i = length1 - length2; i > 0; i--) { plist1 = plist1->_next; } } else { for (i = length2 - length1; i > 0; i--) { plist2 = plist2->_next; } } while (plist1 != plist2) { plist1 = plist1->_next; plist2 = plist2->_next; } return plist1; } if (EntryNode1 != EntryNode2) { return EntryNode1; } }一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。
复杂链表的节点结构:
typedef struct ComplexNode { DataType _data; struct ComplexNode *_next; struct ComplexNode *_random; }CNode,*pCNode,*pCList;复杂链表的复制,主要是节点之间指向的变化问题,只要掌握方法,实现起来并不复杂。
pCNode CreateComplexNode(DataType d) { pCNode newNode = (pCNode)malloc(sizeof(pCNode)); if (newNode == NULL) { perror("malloc"); return NULL; } newNode->_data = d; newNode->_next = NULL; newNode->_random = NULL; return newNode; } void PrintComplexList(pCList pclist) { pCNode cur = pclist; if (pclist == NULL) return; while (cur) { printf("[%d]->random:", cur->_data); if (cur->_random) { printf("%d--->", cur->_random->_data); } else { printf("NULL--->"); } cur = cur->_next; } } //复杂链表的复制 pCList CloneComplexList(pCList pclist) { pCNode cur = pclist; pCNode cloneNode = NULL; pCList l1 = NULL; pCList l2 = NULL; pCNode tail = NULL; if (pclist == NULL) return NULL; //1.拷贝原始链表的每一个节点,连到当前节点后 while (cur) { cloneNode = CreateComplexNode(cur->_data); cloneNode->_next = cur->_next; cur->_next = cloneNode; cur = cur->_next->_next; } //2.调整random指针 cur = pclist; while (cur) { cur->_next->_random = cur->_random->_next; cur = cur->_next->_next; } //3.分离两条链表 cur = pclist; l1 = cur; l2 = cur->_next; tail = l2; while (cur) { cur->_next = tail->_next; if (cur->_next == NULL) { tail->_next == NULL; break; } tail->_next = cur->_next->_next; cur = cur->_next; tail = tail->_next; } return l2; }前提:两个单链表有序且各自无重复的节点 算法思想与合并两个有序单链表如出一辙,些许不同的地方会在注释中阐明。
//求两个单链表交集 pList Intersection(pList plist1, pList plist2) { pNode newList = BuyNode(0); pNode cur = newList; if (plist1 == NULL || plist2 == NULL) return NULL; //相等同时走并把结点保存,不相等小的那个走 while (plist1 && plist2) { if (plist1->_data == plist2->_data) { cur->_next = plist1; plist1 = plist1->_next; plist2 = plist2->_next; cur = cur->_next; } else if (plist1->_data < plist2->_data) plist1 = plist1->_next; else plist2 = plist2->_next; } cur->_next = NULL; return newList->_next; } //求两个单链表差集 pList DifferenceSet(pList plist1, pList plist2) { pNode newList = BuyNode(0); pNode cur = newList; if (plist1 == NULL || plist2 == NULL) return NULL; //不相等保存小的且小的向后走,相等直接同时向后走 while (plist1 && plist2) { if (plist1->_data != plist2->_data) { if (plist1->_data < plist2->_data) { cur->_next = plist1; plist1 = plist1->_next; cur = cur->_next; } else { cur->_next = plist2; plist2 = plist2->_next; cur = cur->_next; } } else { plist1 = plist1->_next; plist2 = plist2->_next; } } //将cur->置空 cur->_next = NULL; //若还有结点剩余,再链在cur后 if (plist1) cur->_next = plist1; if (plist2) cur->_next = plist2; return newList->_next; }