您的位置:首页 > 理论基础 > 数据结构算法

数据结构---单链表

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. }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单链表 数据结构