数据结构---单链表
2016-05-23 18:09
447 查看
1. 已知链表的头结点head,写一个函数把这个链表逆序
1. void List::reverse() 2. { 3. list_node * p = head; 4. list_node * q = p->next; 5. list_node * r = NULL; 6. while(q){ 7. r = q->next; 8. q->next = p; 9. p = q; 10. q = r; 11. } 12. head->next = NULL; //head仍指向逆序前的第一个元素 13. head = p; 1. } 2. void List::reverse2(list_node * curnode) 3. { 4. if(curnode ==NULL)curnode = head; 5. if(curnode->next==NULL) 6. { 7. cur = curnode; 8. return; 9. } 10. 11. reverse2(curnode->next); 12. curnode->next->next=curnode; 13. if(curnode == head) 14. { 15. head=cur; 16. cur = curnode; 17. cur->next = NULL; 18. } 19. }
2. 已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序
1. void List::merge(List & list) 2. { 3. list_node * a = head; 4. list_node * b = list.head; 5. list_node * tempa= a; 6. list_node * tempb = b; 7. while(a&&b) 8. { 9. if(a->value <= b->value) 10. { 11. while(a&&b&&a->value <= b->value) 12. { 13. tempa = a; 14. a = a->next; 15. } 16. tempa->next=b; 17. } 18. else 19. { 20. while(a&&b&&a->value > b->value) 21. { 22. tempb = b; 23. b = b->next; 24. } 25. tempb->next=a; 26. } 27. 28. } 29. } 1. list_node* List::recursive_merge(list_node * a,list_node * b) 2. { 3. if(a == NULL)return b; 4. if(b == NULL)return a; 5. if(a->value <= b->value){ 6. a->next=recursive_merge(a->next,b); 7. return a; 8. } 9. if(a->value > b->value) 10. { 11. b->next=recursive_merge(a,b->next); 12. return b; 13. } 14. } 1. //没有创建新链表,只是将元素链接起来 2. Node * Merge(Node *head1 , Node *head2) 3. { 4. if ( head1 == NULL) 5. return head2 ; 6. if ( head2 == NULL) 7. return head1 ; 8. Node *head = NULL; 9. Node *p1 = NULL; 10. Node *p2 = NULL; 11. if ( head1->data < head2->data )//先确定head 12. { 13. head = head1; 14. p1 =head1->next; 15. p2 =head2; 16. } 17. else 18. { 19. head = head2 ; 20. p2 =head2->next ; 21. p1 =head1 ; 22. } 23. Node *pcurrent = head ; 24. while ( p1 != NULL && p2 != NULL) //处理剩余元素 25. { 26. if ( p1->data <= p2->data ) 27. { 28. pcurrent->next = p1 ; 29. pcurrent = p1 ; 30. p1 = p1->next ; 31. } 32. else 33. { 34. pcurrent->next = p2 ; 35. pcurrent = p2 ; 36. p2 = p2->next ; 37. } 38. } 39. if ( p1 != NULL ) 40. pcurrent->next = p1 ; 41. if ( p2 != NULL ) 42. pcurrent->next = p2 ; 43. return head ; 44. } 1. Node * MergeRecursive(Node *head1 , Node *head2) 2. { 3. if ( head1 == NULL ) 4. return head2 ; 5. if ( head2 == NULL) 6. return head1 ; 7. Node *head = NULL ; 8. if ( head1->data < head2->data ) 9. { 10. head = head1 ; 11. head->next = MergeRecursive(head1->next,head2); 12. } 13. else 14. { 15. head = head2 ; 16. head->next = MergeRecursive(head1,head2->next); 17. } 18. return head ; 19. }
3. 找出单向链表中中间结点
两个指针,一个步长为1,另一个步长为2.步长为2的走到底后步长为1的正好到中间1. list_node * List::middleElement() 2. { 3. list_node * p = head; 4. list_node * q = head->next; 5. while(q){ 6. p = p->next; 7. if(q)q=q->next; 8. if(q)q=q->next; 9. } 10. return p; 11. }
4. 如何检查一个单向链表上是否有环
同样两个指针,一个步长为1,另一个步长为2,如果两个指针能相遇则有环。1. list_node * List::getJoinPointer() 2. { 3. 4. if(head == NULL || head->next == NULL)return NULL; 5. list_node * one = head; 6. list_node * two = head->next; 7. while(one != two){ 8. one = one->next; 9. if(two)two=two->next; 10. else break; 11. if(two)two=two->next; 12. else break; 13. } 14. if(one == NULL || two == NULL) return NULL; 15. return one; 16. }
5. 只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。将p后面那个节点的值复制到p,删除p后面的节点
1. # 包含删除最后一个节点的情况 2. ListNode* DeleteNode(ListNode* pHead, ListNode* pDeleted) 3. { 4. if(pHead == NULL || pDeleted == NULL) 5. return NULL; 6. 7. if(pDeleted->next != NULL) //不是最后一个结点 8. { 9. ListNode *pNext = pDeleted->next; //删除的其实是下一个结点 10. //下面两步是关键 11. pDeleted->key = pDeleted->next->key; 12. pDeleted->next = pDeleted->next->next; 13. delete pNext; 14. pNext = NULL; 15. } 16. else 17. { 18. if(pHead == pDeleted) //删除结点既是第一个结点也是最后一个结点 19. { 20. pHead = NULL; 21. } 22. else 23. { 24. ListNode *pNode = pHead; 25. while(pNode->next != pDeleted) //找删除结点的前驱 26. pNode = pNode->next; 27. pNode->next = pDeleted->next; 28. } 29. delete pDeleted; 30. pDeleted = NULL; 31. } 32. return pHead; 33. }
6. 给定单链表头结点,删除链表中倒数第k个结点
一个指针指向链表头,另一个指针指向第k个指针,然后两个指针一起移动,第二个指针到了末端则第一个指针就是倒数第k个节点1. # 判断节点个数小于K的情况 2. list_node * List::lastKelement(int k){ 3. int t = k; 4. list_node * p = head; 5. while(p&&t){ 6. p=p->next; 7. t--; 8. } 9. if(p == NULL && t >0)return NULL; 10. list_node * q=head; 11. while(q && p){ 12. p=p->next; 13. q=q->next; 14. } 15. return q; 16. }
7. 输入一个链表的头结点,从尾到头反过来输出每个结点的值
思路:有很多解法,网上常见的有三种,一般都能想到。(1)可以利用问题1的结果,反转链表,然后再输出。(2)用递归实现。(3)用栈,其实与解法2相似,递归的本质其实就是栈的思想。给出后两种解法的代码。1. //栈实现 2. void ReorderPrint_Solution1(ListNode * pHead) 3. { 4. stack<int> keyStack; //结点的数据栈 5. ListNode *pNode = pHead; 6. 7. while(pNode != NULL) //将链表的数据压栈 8. { 9. keyStack.push(pNode->key); 10. pNode = pNode->next; 11. } 12. while(keyStack.size() > 0) //出栈 13. { 14. cout<<keyStack.top()<<' '; 15. keyStack.pop(); 16. } 17. } 18. //递归实现 19. void ReorderPrint_Solution2(ListNode * pHead) 20. { 21. if(pHead == NULL) 22. return; 23. else 24. { 25. ReorderPrint_Solution2(pHead->next); 26. cout<<pHead->key<<' '; 27. } 28. }
8. 两个单向链表,找出它们的第一个公共结点
思路:(1)可以利用散列来做,根据第一个链表的地址,建立一张散列表,然后针对第二个链表的每一个结点,查询散列表,如果找到,那么这个结点就是第一个公共的结点。这种方法的时间复杂度为O(Len(h1)+Len(h2)),空间复杂度为O(Len(h1))。(2)何海涛给出的,非常巧妙,时间复杂度为O(Len(h1)+Len(h2)),空间复杂度为O(1)。一个重要的思想就是,如果两个链表等长,那么让它们同时向前移动,一定能同时到达第一个公共结点,能想到这一点,解法自然而然就有了。所给的两个链表不一定等长,只需遍历一下,分别求出链表的长度,然后将长链表先往前移动两者的长度之差,再同时向前。下面给出这两种方法的具体算法。1. ListNode* FindFirstCommonNode_Solution1(ListNode *pHead1, ListNode *pHead2) 2. { 3. set<ListNode *> addrSet; //这里用集合代替散列 4. 5. //将链表的结点地址放入集合中 6. ListNode *pNode = pHead1; 7. while(pNode != NULL) 8. { 9. addrSet.insert(pNode); 10. pNode = pNode->next; 11. } 12. 13. //开始寻找 14. pNode = pHead2; 15. while(pNode != NULL) 16. { 17. if(addrSet.find(pNode) != addrSet.end()) 18. break; 19. pNode = pNode->next; 20. } 21. return pNode; 22. } 1. //求链表长度 2. int LengthOfList(ListNode *pHead) 3. { 4. int len = 0; 5. ListNode *pNode = pHead; 6. while(pNode != NULL) 7. { 8. len++; 9. pNode = pNode->next; 10. } 11. return len; 12. } 13. 14. //用何海涛的方法 第一个共同点为交点????? 15. ListNode* FindFirstCommonNode_Solution2(ListNode *pHead1, ListNode *pHead2) 16. { 17. int len1 = LengthOfList(pHead1); 18. int len2 = LengthOfList(pHead2); 19. //确定长链表 20. int lenDiff = len1 - len2; 21. ListNode *pLong = pHead1; 22. ListNode *pShort = pHead2; 23. if(len1 < len2) 24. { 25. pLong = pHead2; 26. pShort = pHead1; 27. lenDiff = len2 - len1; 28. } 29. //调整长链表的长度 30. for(int i = 0; i < lenDiff; i++) 31. pLong = pLong->next; 32. 33. ListNode *pNode = NULL; 34. while(pShort != NULL) 35. { 36. if(pShort == pLong) //找到第一个共同结点 37. { 38. pNode = pShort; 39. break; 40. } 41. pShort = pShort->next; 42. pLong = pLong->next; 43. } 44. return pNode; 45. }
9. 有一个单链表,其中可能有一个环,也就是某个节点的next指向的是链表中在它之前的节点,这样在链表的尾部形成一环。如果链表存在环,找到环的入口点?
思路:这道题的原型可能来自《C专家编程》一书,题目为”怎样才能检测到链表中存在循环“,书中给出的最终算法是定义两个指针p1,p2,p1每次移动一个位置,而p2每次移动两个位置,这样如果链表中存在循环,那么p2一定能追上p1。如果不存在,那么p2会到达链表尾部,即检测到空。这个比较简单,但是如果要求找出环的入口点呢?本文给出两种思路。第一种需要辅助空间,可以用散列表,用来存放结点的地址(不能存放结点值,值会重复)。定义一个指针用来遍历链表,假设指针当前指向 i 结点,检查散列表中是否含有该结点,如果有则该结点就是环的入口点。否则,将该结点的地址加入散列表中。如果链表不存在环,那么最终会到达链表末尾,从而跳出循环。实现中,由于标准库中没有hash,因此用集合代替,意思差不多。时间复杂度上会有影响,用散列表为O(n),而用集合为O(nlogn),而空间复杂度为O(n)。
第一种方法需要O(n)的辅助空间,如果要求辅助空间为O(1),应该怎么办呢?容易想到的就是用穷举法,对于每个结点,检查该结点是否是环的入口点。可定义两个指针,p1和p2。p1每次往前移动一步,表示当前被检查的结点。p2每次从头部开始往前移动,当p1和p2相等时,检查两个指针通过的距离,如果距离一样,表示该结点不是环入口点,距离不一样,表示p1已经在环中绕了一圈,第二次到达入口点,而p2是第一次到达入口点,此时两者所指的结点即为环的入口点。这种方法的时间复杂度为O(n^2),空间复杂度为O(1)。
1. //函数功能 : 找含单链表的环入口点 2. //函数参数 : pHead指向链表首部 3. //返回值 : 返回的是环的入口点,如果不存在环,返回NULL 4. ListNode* FindFirstCrossNode_Solution1(ListNode * pHead) 5. { 6. set<ListNode *> addrSet; //这里用集合代替散列,会影响时间复杂度 7. ListNode *pNode = pHead; 8. while(pNode != NULL) 9. { 10. if(addrSet.find(pNode) == addrSet.end()) 11. addrSet.insert(pNode); 12. else 13. break; 14. pNode = pNode->next; 15. } 16. return pNode; 17. } 1. ListNode* FindFirstCrossNode_Solution2(ListNode * pHead) 2. { 3. ListNode *p1 = pHead; 4. int pos1 = 0; 5. while(p1) //检查链表的每个结点 6. { 7. ListNode *p2 = pHead; 8. int pos2 = 0; 9. while(p2) //每次都从头开始 10. { 11. if(p1 == p2) //两个指针指向的结点一样,可能是相交也可能不相交 12. { 13. if(pos1 == pos2) //两个指针走过的距离一样 14. break; 15. else //p1在环中绕了一圈,导致pos1与pos2不一样 16. return p1; 17. } 18. p2 = p2->next; //前进一个位置 19. pos2++; //记录走过的距离 20. } 21. p1 = p1->next; 22. pos1++; 23. } 24. return NULL; 25. }
10. 判断两个链表是否相交
两种情况,如果链表有环,则先在环里设定一个指针不动,另一个链表从头开始移动,如果另一个链表能够与环中的指针相遇则是相交。如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上;如果没有环,则判断两个链表的最后一个节点是否相同,相同则相交。1. bool isCircle(Node *head, Node *circleNode, Node *lastNode) 2. { 3. Node *fast = head->next; 4. Node *slow = head; 5. while(fast != slow && fast && slow) 6. { 7. if(fast->next != NULL) 8. fast = fast->next; 9. if(fast->next == NULL) 10. lastNode = fast; 11. if(slow->next == NULL) 12. lastNode = slow; 13. fast = fast->next; 14. slow = slow->next; 15. } 16. if(fast == slow && fast && slow) 17. { 18. circleNode = fast; 19. return true; 20. } 21. else 22. return false; 23. } 24. bool isCross(Node *head1, Node *head2) 25. { 26. Node *circleNode1; 27. Node *circleNode2; 28. Node *lastNode1; 29. Node *lastNode2; 30. 31. bool isCircle1 = isCircle(head1,circleNode1,lastNode1); 32. bool isCircle2 = isCircle(head2,circleNode2,lastNode2); 33. 34. if(isCircle1 != isCircle2) //一个有环,一个无环 35. return false; 36. else if(!isCircle1 && !isCircle2) //两个都无环 37. return lastNode1 == lastNode2; 38. else //两个都有环,判断环里的节点是否能到达另一个链表环里的节点 39. { 40. Node *temp = circleNode1->next; 41. while(temp ! = circleNode1) 42. { 43. if(temp == circleNode2) 44. return true; 45. temp = temp->next; 46. } 47. return false; 48. } 49. return false; 50. }
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#定义并实现单链表实例解析
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- C语言实现单链表逆序与逆序输出实例
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C语言单链表常见操作汇总
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- C数据结构之单链表详细示例分析
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- 【数据结构与算法】数组应用4:多项式计算Java版