链表操作 -- 链表自身的处理问题

参考:

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

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

  

问题:

  删除链表中的某个点。

解答:

  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 }

问题:  

  单链表的旋转

解答:  

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

  时间复杂度:O(n)

  空间复杂度:O(1)

  

 1 Node* reverseList(Node* head)
 2 {
 3     if(head == NULL ||head->next == NULL)
 4         return head;
 5
 6     Node* prev = head;
 7     Node* curr = head->next;
 8     Node* next;
 9
10     prev->next=NULL;
11     while(curr != NULL)
12     {
13         next = curr->next;
14
15         curr->next = prev;
16
17         prev = curr;
18         curr = next;
19     }
20
21     return prev;
22 }

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

  耗时操作在于右端指针移动时定位其前一节点的动作。

  总体时间复杂度为:O(n2);空间复杂度为:O(1)

问题:

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

   类似于 Leetcode -- Remove Nth Node from End of List

解答:

  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 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 //快+慢指针啦 ~
13 ListNode *rotateRight(ListNode *head, int k)
14 {
15     if(head == NULL || k<0)
16         return NULL;
17     if(k == 0)
18         return head;
19
20     k = k % get_length(head);
21
22     ListNode* frontPtr = head;
23     while(k--)
24         frontPtr = frontPtr->next;
25
26     ListNode* backPtr = head;
27     while(frontPtr->next)
28     {
29         frontPtr = frontPtr->next;
30         backPtr = backPtr->next;
31     }//最终停止时,frontPtr指向最后一个节点,backPtr指向倒数第k+1个节点。
32
33     frontPtr->next = head;
34
35     head = backPtr->next;
36     backPtr->next = NULL;
37
38     return head;
39 }

  可以通过先移动,后判断的方法来减少不必要的就按链表长度。

 

 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 }
时间: 2024-07-28 12:27:07

链表操作 -- 链表自身的处理问题的相关文章

链表操作法则之逆向遍历与倒置算法

一.创建链表: 对链表进行操作的所有算法的前提,就是我们首先要创建一个链表,我们可以选择正向建链和逆向建链: (一).正向建链: 首先,我们得自定义节点类型: typedef struct Node { int data;//数据域 struct Node * pNext;//指针域 }NODE,*PNODE; 通过数组进行链表数据域的赋值: int main (void) { PNODE pHead;//头指针,接收创建链表时返回的头结点地址 int a[8] = {12,37,49,65,2

数据结构上机测试2-1:单链表操作A (顺序建表+关键字删除)

数据结构上机测试2-1:单链表操作A Time Limit: 1000MS Memory limit: 4096K 题目描述 输入n个整数,先按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据m,将单链表中的值为m的结点全部删除.分别输出建立的初始单链表和完成删除后的单链表. 输入 第一行输入数据个数n: 第二行依次输入n个整数: 第三行输入欲删除数据m. 输出 第一行输出原始单链表的长度: 第二行依次输出原始单链表的数据: 第三行输出完成删除后的单链表长度: 第四行依次输出完成删除后的

单链表操作

#include<stdio.h>#include<malloc.h> typedef struct Node{ int data; struct Node *next;}LinkList; //就地反转int LinkListRerverse(LinkList *head){ LinkList *q,*p; p = head->next; head->next = NULL; while(p != NULL){ q = p->next; p->next =

数据结构之链表操作

#include <IOSTREAM> #include <IOMANIP> #include <STRING> using namespace std; typedef struct Student { int id; char name[20]; char sex[20]; struct Student *next; }node; typedef node * pnode; pnode Create(); void Print(pnode h); void Sort

单向链表操作 原文:http://canlynet.blog.163.com/blog/static/255013652009113001041903/

/********************************************** 功能:单向链表操作(注意Head指针 需要被改变时传入的是二级指针) 日期:2009.12.29 作者:DC ***********************************************/ #include <stdio.h> #include <stdlib.h> #define OVER_FLOW -2 #define OK 0 typedef int ElemTy

单链表操作系列

#include<stdio.h> #include<stdlib.h> typedef int ElemType; //定义结点类型 typedef struct Node { ElemType data; struct Node *next; }LNode,*LinkList; //单链表的建立1,头插法建立单链表,逆序生成 LinkList LinkListCreateH() { LinkList L,p; L = (LinkList)malloc(sizeof(LNode)

链表操作 -- 有环链表问题

参考: http://blog.163.com/[email protected]/blog/static/1113522592011828104617420/ 问题: 判断一个链表中是否有环. 分析: 我们都知道,当一个链表中没有环时,我们使用一个指针能从头遍历到尾:当链表中有环时,链表会在环中旋转. 当我们只使用一个链表指针时,可想到方法就是使用额外的数据结构来存储遍历过的每个节点,在遍历next节点时,判断next节点是否已存在于存储的节点中. 存储结构可以选择为hashTable,这样的

C# 链表操作

关于链表操作,在C#当中微软已经提供了一个LinkedList<T>的数据结构,通过这个类提供的一系列方法就能够实现链表操作. 这里我提供一段代码,这是在论坛里面有人提问时给出的代码,它实现了自定义链表的操作(读者可以在此基础上进一步完善).因为这段代码涉及一些C#技巧,所以贴出来给初学者学习C#提供一点参考. 实体类:     /// <summary>     /// 学生类     /// </summary>     public class Student  

链表操作 -- 问题总结贴

本文同时收集了好友 sosohu 和 zhuoyuan 的文章,共同进步.欢迎指正. 链表结构的实现: -- zhouyuan 单链表操作: (1). 倒序访问 --  sosohu (2). 获取链表的倒数第K个元素 -- sosohu    zhouyuan carlsama (3). 查找链表的中间节点 -- sosohu  carlsama (4). 链表反转 -- sosohu zhuoyuan carlsama (5). 链表中节点的删除 -- sosohu  carlsama (6