zoukankan      html  css  js  c++  java
  • 链表操作 -- 链表自身的处理问题

    问题:

      删除链表中的某个给定点。 

    解答:

      1)可以借助于节点的前一个节点来删除。

        要花费O(n)的时间来查找节点的前继节点。时间复杂度O(n),空间复杂度O(1).

      2)将后一个节点的内容复制到本节点,然后删除后一个节点。时间复杂度O(1),空间复杂度O(1)

        这样就省略了查找上一个节点的时间。注意要删除的节点为尾节点的情况!      

     1 Node* DelNode(Node* node,Node* head)
     2 {
     3     if(node->next == NULL)//尾节点要特殊处理
     4     {
     5         while(head->next != node)
     6             head = head->next;
     7 
     8         head->next = NULL;
     9         return NULL;
    10     }else
    11     {
    12         *node = *(node->next);
    13         node->next = node->next->next;
    14         return node;
    15     }
    16 }

    问题:  

      单链表的区间反转(reverse)

      题目:Swap Nodes in Pairs   Reverse Linked List II  

    解答:  

      1)每次取得两个相邻的三个节点,反转前两个节点的指针,记录第三个节点。依次往后移动。

      时间复杂度:O(n)     空间复杂度:O(1)  

      

     1 //We reverse the node in (prev,end] { prev != end , end != NULL}
     2 //When solving particular problems,
     3  //a guard head can be generated to eliminate special verdict for head node.
     4  void reverse(ListNode* prev,ListNode* end)
     5  {
     6     ListNode* l = prev->next;//A head for new reversed list
     7     ListNode* n = l->next;//Old remained list
     8     ListNode* nn;
     9 
    10     ListNode* nend = end->next;
    11     while(l != end)
    12     {
    13         nn = n->next;
    14 
    15         n->next = l;//n became the new head
    16 
    17         l = n;
    18         n = nn;
    19     }
    20 
    21     prev->next->next = nend;
    22     prev->next = end;
    23 }

      2)使用两个指针,一直指向最左端,一个指向最右端。交换两个指针指向对象的内容,之后左端指针右移,右端指针左移。

      耗时操作在于右端指针移动时定位其前一节点的动作。  总体时间复杂度为:O(n2);空间复杂度为:O(1)

      

      更新:  

        3)递归

        4)一直插在新链表头部后面(头插法)

    问题:

      找出链表中的倒数第n个元素。

        类似题目:Remove Nth Node from End of List

    解答:

      更新:

      0)使用递归回溯解决。

      1)两个遍历解决。注意:n大于链表长度的情况。

      时间复杂度:O(n),空间复杂度:O(1)

      如何一次遍历就解决问题?

      2)使用连个指针【又是双指针!】。选择一个指针向前先走n步,然后两个指针同时移动。当先移动的指针到达链表尾部的时候,另一个指针就指在倒数第n个位置。

      时间复杂度:O(n),空间复杂度:O(1)

     1 Node* find_backwark_n(Node* head,int n)
     2 {
     3     if(n <= 0)
     4         return NULL;
     5 
     6     Node* frontNPtr = head;
     7     while(n-- && frontNPtr)//这里要注意一点:当最后退出时会判断到n==0,虽然此时会退出while循环,但是n--操作还是会执行,n的值变为了-1
     8         frontNPtr = frontNPtr->next;
     9 
    10     //对于n大于链表长度的情况我们返回NULL!
    11     //如果head == NULL的情况,也会在这里返回
    12     if(n >= 0)
    13         return NULL;
    14 
    15     Node* backNPtr = head;
    16 
    17     while(frontNPtr)
    18     {
    19         frontNPtr = frontNPtr->next;
    20         backNPtr = backNPtr->next;
    21     }
    22 
    23     return backNPtr;
    24 }

      3)借助一个数组来模拟的滑动窗口。窗口的大小为n,窗口在滑动的时候,剔除链表左端的元素,加入右端新元素。

      这样当遍历完数组后,窗口中记录的链表中最左端元素即为所求。

      时间复杂度:O(n),空间复杂度:O(m)

     1 Node* find_backwark_n(Node* head,int n)
     2 {
     3     if(n <= 0 || head == NULL)
     4         return NULL;
     5 
     6     Node** window = new Node*[n];
     7     if(window==NULL)
     8         return NULL;
     9 
    10     Node* curr = head;
    11     int pos = 0;
    12     while(curr)
    13     {
    14         window[pos % n] = curr;
    15 
    16         pos++;
    17         curr = curr->next;
    18     }
    19 
    20     if(pos < n)
    21         return NULL;
    22 
    23     Node* res = window[pos % n];
    24     delete window;
    25 
    26     return res;
    27 }

    问题:

      寻找链表的中间元素。

    解答:

      1)两次遍历。

      2)快+慢指针。

      寻找链表的中间元素,也就是寻找链表中位于1/2位置的元素。我们考虑下,寻找链表中1/n位置的元素。

      此时仍用快慢指针来处理。只不过快指针每次向前移动n步。

      时间复杂度:O(n),空间复杂度:O(1)

     1 Node* get_position(Node* node,int n)
     2 {
     3     while(n-- && node)
     4         node = node->next;
     5     if(n >=0)
     6         return NULL;
     7 
     8     return node;
     9 }
    10 Node* find_Node(Node* head,int n)
    11 {
    12     if(n <= 1)
    13         return NULL;
    14 
    15     Node* fast = get_position(head,n);
    16     if(fast == NULL)
    17         return NULL;
    18 
    19     Node* slow = head->next;
    20     while(fast = get_position(fast,n))
    21     {
    22         slow = slow->next;
    23     }
    24 
    25     return slow;
    26 }

    问题:

      交换链表中”相邻“的节点。只能通过操作节点的next指针来实现。

      题目:Leetcode -- Swap Nodes in Pairs

    解答:

      可以申请使用newHead节点来减少边界判断!

     1 ListNode* swapPairs(ListNode *head)
     2 {
     3     //可以通过使用一个新头来减少对左边界情况的判断
     4     ListNode newHead(0);
     5     ListNode* p = &newHead;
     6     p->next = head;
     7 
     8     //我们在代码中为新的nnext节点连向了nnnext节点
     9     //所以不需要处理只剩下单个节点的情况了。
    10     while(p->next && p->next->next)
    11     {
    12         ListNode* n = p->next;
    13         ListNode* nnn = p->next->next->next;
    14 
    15         p->next = p->next->next;
    16         p->next->next = n;
    17         p->next->next->next = nnn;
    18 
    19         p = p->next->next;
    20     }
    21 
    22     return newHead.next;
    23 }

    问题:

      旋转链表n位。  题目: Leetcode -- Rotate List

    解答:

      遍历一遍后,将链表收尾相连,可以减少边界判断!

      1)参考动态窗口的思路。注意窗口为1的情况需要特别处理。

     1     int get_length(ListNode* head)
     2     {
     3         int len = 0;
     4         while(head)
     5         {
     6             len++;
     7             head = head->next;
     8         }
     9         return len;
    10     }
    11     
    12     //使用k的数组存储后[k+1 ~ 2]个值
    13     //思路来自“寻找单链表中的倒数第n个数”
    14     ListNode *rotateRight(ListNode *head, int k)
    15     {
    16         if(head == NULL)
    17             return NULL;
    18     
    19         k = k % get_length(head);//在使用get_length之前,得确认链表是否为空
    20         if(k==0)
    21             return head;
    22     
    23         ListNode** window = new ListNode*[k];
    24     
    25         ListNode* curr = head;
    26         int pos = 0;
    27         while(curr->next)
    28         {
    29             window[pos % k] = curr;
    30             curr = curr->next;
    31             pos ++ ;
    32         }
    33     
    34         //倒数第k+1个节点被存储在window[pos % (k+1)]中
    35         //最后一个节点位于curr中
    36     
    37         curr->next = head;
    38         window[pos % k]->next = NULL;
    39         
    40         if(k == 1)
    41             return curr;
    42         else
    43             return window[(pos+1) % k];
    44     }

      2)既然归根到底是定位的问题,当然可以用块+慢指针啦  

     1 //快+慢指针啦 ~
     2 ListNode *rotateRight(ListNode *head, int k)
     3 {
     4     if(head == NULL || k<=0)
     5         return head;
     6 
     7     ListNode* frontPtr = head;
     8     int pos = 0;
     9     while(pos < k)
    10     {
    11         pos++;
    12         frontPtr = frontPtr->next;
    13         if(frontPtr == NULL)
    14             break;
    15     }
    16 
    17     if(frontPtr == NULL)
    18         return rotateRight(head,k%pos);//可以通过先移动,后判断的方法来减少不必要的就按链表长度。
    19 
    20     ListNode* backPtr = head;
    21     while(frontPtr->next)
    22     {
    23         frontPtr = frontPtr->next;
    24         backPtr = backPtr->next;
    25     }//最终停止时,frontPtr指向最后一个节点,backPtr指向倒数第k+1个节点。
    26 
    27     frontPtr->next = head;
    28 
    29     head = backPtr->next;
    30     backPtr->next = NULL;
    31 
    32     return head;
    33 }

    问题:

      Partition List

    解答:

    ListNode *partition(ListNode *head, int x)
    {
        if(head == NULL || head->next == NULL)
            return head;
    
        ListNode lNode(0),* l;
        l = &lNode;
    
        ListNode ulNode(0),*ul;
        ul = &ulNode;
    
        ListNode* curr = head;
        while(curr)
        {
            if(curr->val < x)
            {
                l->next = curr;
                l = l->next;
            }else
            {
                ul->next = curr;
                ul = ul->next;
            }
    
            curr = curr->next;
        }
    
        l->next = ulNode.next;
        ul->next = NULL;//We need to assure the last elem of list point to NULL
    
        return lNode.next;
    }

    问题:

      Reorder List

    解答:

      本问题可以思考为将原链表从中间拆为两份,reverse后一份链表后,重新合并。

      

    参考:

      http://blog.csdn.net/huangxy10/article/category/1244320这位博友的链表专题

      和别人的leetcode代码,在此致谢。

  • 相关阅读:
    【字符串/广搜】P1032 字串变换
    【动态规划】P1541 乌龟棋
    【动态规划/递归】(团队内部比赛试题)T134293 T2.货币系统问题
    Redis-事务
    Redis-Pipeline
    Redis-通过前缀获取所有key
    Spring 三级缓存
    TopK_LRU_归并

    如何从一亿个数组里找出最大的十个
  • 原文地址:https://www.cnblogs.com/carlsama/p/4127216.html
Copyright © 2011-2022 走看看