Leetcode 单链表反转

单链表反转的题目很基本,借此题又把相关知识回顾了一边。另外需要强调的是C#中的static方法和成员,即它们为类所有,为类的各个实例所公用,无论创建了多少实例,类的静态成员在内存中只有一份,而实例化的方法每次都会创建一份新的内存区域,但是会自动销毁。静态方法不用创建任何对象,也可以直接调用,所以创建后不能自动销毁。最后就是分享一篇详细讲解单聊表的博文:http://www.cnblogs.com/xiexiaohua/archive/2010/09/01/1809250.html;

引言:用C#把数据结构单链表基本操作部分实现了一遍,并做了个小总结,现发布出来,欢迎各位大虾拍砖!谢谢!

一、链表描述

单链表在现实生活中可以找到例子,如自行车的链条,它由若干个链子组成,每个链子由一个铁圈和一个铁栓组成,链子就好比链表的结点,铁圈就好比链表的数据域,铁栓好比链表的引用域。

链条(链表)

链子(结点)

单链表(LinkedList):
链表是用一组任意的存储单元来存储线性表中的数据元素。这组存储单元可以是连续的,也可以是不连续的。

结点(Node):
数据域,存储数据元素;
引用域,存储与它相邻的数据元素的存储地址信息。


线性表通过每个结点的引用域形成了一根“链条”

 

二、结点类和链表类定义

(说明:为了演示的简便,结点的数据域定义为int型了,大家可以改为其它数据类型或者扩展为泛型)

1、单链表结点类定义:

 
复制代码
 1       ///   <summary>
 2       ///  单链表节点类
 3       ///   </summary>
 4       public   class  Node
 5      {
 6           public  Node()
 7          {
 8              data  =   0 ;
 9              next  =   null ;
10          }
11           public  Node( int  val,Node p)
12          {
13              data  =  val;
14              next  =  p;
15          }
16           public  Node( int  val)
17          {
18              data  =  val;
19              next  =   null ;
20          }
21           public  Node(Node p)
22          {
23              data  =   0 ;
24              next  =  p;
25          }
26 
27           private   int  data;
28           private  Node next;
29           public   int  Data
30          {
31               get
32              {
33                   return  data;
34              }
35               set
36              {
37                  data  =  value;
38              }
39          }
40           public  Node Next
41          {
42               get
43              {
44                   return  next;
45              }
46               set
47              {
48                  next  =  value;
49              }
50          }
51      }
复制代码

2、单链表类定义:

 
复制代码
 1       ///   <summary>
 2       ///  单链表类
 3       ///   </summary>
 4       public   class  LinkedList
 5      { 
 6           public  LinkedList()
 7          {
 8              head  =   null ;
 9          }
10           private  Node head;
11           public  Node Head
12          {
13               get
14              {
15                   return  head;
16              }
17               set
18              {
19                  head  =  value;
20              }
21          }
22            // 创建操作(头插和尾插两种)
23            // 查找操作(按值和按序号两种)
24            // 插入操作(结点前插入、结点后插入和指定位置插入)
25            // 删除操作(按值和按序号两种)
26            // 遍历操作
27            // 反转操作
28            // 求长度操作
29        }
复制代码

 

三、链表基本操作算法

链表的基本操作包括七大操作,创建操作(头插和尾插两种)、查找操作(按值和按序号两种)、插入操作(结点前插入、结点后插入和指定位置插入)、删除操作(按值和按序号两种)、遍历操作反转操作求长度操作

1、创建操作

1.1头插法创建链表

头插法是在链表的头部插入结点建立单链表,建立单链表从空表开始,每读入一个数据元素就申请一个结点,然后插在链表的头部。

复制代码
 1          // 创建操作(头插法创建链表)
 2           public   void  CreateListHead()
 3          {  
 4               int  d  =  Int32.Parse(Console.ReadLine());
 5               while  (d  !=   - 1 )
 6              {
 7                  Node p  =   new  Node(d);
 8                  p.Next  =  head;
 9                  head  =  p;
10                  d  =  Int32.Parse(Console.ReadLine());
11              }
12          }
复制代码

1.2 尾插法创建链表

头部插入结点建立单链表读入的数据元素的顺序与生成的链表中元素的顺序是相反的。若希望次序一致,则用尾部插入的方法。因为每次是将新结点插入到链表的尾部,所以需加入一个引用P用来始终指向链表中的尾结点,以便能够将新结点插入到链表的尾部 。

 

 
复制代码
 1           ///   <summary>
 2           ///  创建操作(尾插法创建链表)
 3           ///   </summary>
 4           public   void  CreateListTail()
 5          {
 6              Node p  =  head;
 7               int  d  =  Int32.Parse(Console.ReadLine());
 8               while (d !=- 1 )
 9              {
10                  Node s  =   new  Node(d);
11                   if  (head  ==   null )
12                  {
13                      head  =  s;
14                  }
15                   else
16                  {
17                      p.Next  =  s;
18                  }
19                  p  =  s;
20                  d  =  Int32.Parse(Console.ReadLine());
21              }
22          }
复制代码

2.查找操作

2.1 按值查找链表

单链表中的按值查找是指在表中查找其值满足给定值的结点,并返回该结点。
思路:通过引用P遍历,直到P没有到达表尾之后,如果当前结点数据与关键字相符,返回该结点,否则p向后移动,准备查找下一个结点,如果所有结点都不匹配,则返回null。
等价精简代码的思路是当循环满足p没有到达表尾之后和当前结点数据不等于关键字两个条件时,p向后移动,如果找到关键字,循环终止,返回p指向的当前结点,即返回p;如果一直找不到,即p已近移动到了表尾之后,p的值为null了,返回p也就是返回null。

 
复制代码
 1           ///   <summary>
 2           ///  查找操作(按值查找链表)
 3           ///   </summary>
 4           ///   <param name="key"> 待查找节点的值 </param>
 5           ///   <returns> 节点 </returns>
 6           public  Node GetNodeByVal( int  key)
 7          {
 8              Node p  =  head;
 9               while  (p  !=   null )
10              {
11                   if  (p.Data  ==  key)
12                  {
13                       return  p;
14                  }
15                  p  =  p.Next;
16              }
17               return   null ;
18               // ---以下为等价精简代码---
19               // Node p = head;
20               // while (p != null && p.Data != key)
21               // {
22               //     p = p.Next;
23               // }
24               // return p;
25          }
复制代码

2.2 按序号查找链表

通过指定的序号查找链表中对应的结点。
思路和按值查找类似。

 
复制代码
 1           ///   <summary>
 2           ///  查找操作(按序号查找链表)
 3           ///   </summary>
 4           ///   <param name="i"> 待查找节点序号 </param>
 5           ///   <returns> 节点 </returns>
 6           public  Node GetNodeByIndex( int  i)
 7          {
 8              Node p  =  head;
 9               int  j  =   1 ;
10               while  (p  !=   null )
11              {
12                   if  (j  ==  i)
13                  {
14                       return  p;
15                  }
16                  j ++ ;
17                  p  =  p.Next;
18              }
19               return   null ;
20               // ---以下为等价精简代码---
21               // Node p = head;
22               // int j = 1;
23               // while (p != null && j < i)
24               // {
25               //     p = p.Next;
26               //     j++;
27               // }
28               // return p;
29          }
复制代码

3.插入操作

3.1指定节点之后插入新结点

给定一个关键结点的值,在该结点的后面插入一个新结点。

思路:通过新结点的值构造新结点,通过关键字的值找到关键结点的引用;如果找不到关键结点或者空表的话给出提示,否则执行新结点的插入操作,插入操作是先连(新结点先连到关键结点的后继结点,s.Next = q.Next;),后断(断开关键结点和原后继结点的连接,使其连到新结点,q.next=s),

 
复制代码
 1           ///   <summary>
 2           ///  插入操作(指定节点之后插入新节点)
 3           ///   </summary>
 4           ///   <param name="key"> 指定节点的值 </param>
 5           ///   <param name="x"> 新节点的值 </param>
 6           public   void  InsertAfter( int  key,  int  x)
 7          {
 8              Node s  =   new  Node(x);
 9              Node q  =  GetNodeByVal(key);
10               if  (head  ==   null   ||  q  ==   null )
11              {
12                  Console.WriteLine( " The linked list is empty or position is error! " );
13              }
14               else
15              {
16                  s.Next  =  q.Next;
17                  q.Next  =  s;
18              }
19          }
复制代码

3.2 指定结点之前插入新结点

给定一个关键结点的值,在该结点的前面插入一个新结点。

思路:要插入一个新结点,需要先找前继结点,这样操作就和上面的后插结点类似了。可是这里给的是后继结点,怎么办,我们需要定义一个引用p从表头不断后移直到指向关键结点的前继结点,即通过p.next!=q循环判断p的后继结点是否是关键结点,如果不是,p继续后移,直到找到关键结点,此时p结点就是关键结点的前继结点,也就是相当于在p之后插入新结点了,然后先连后断,这里有一个特殊情况得单独处理,就是当关键结点是第一个结点时,因为此时关键结点没有前继结点。

 
复制代码
 1           ///   <summary>
 2           ///  插入操作(指定节点之前插入新节点)
 3           ///   </summary>
 4           ///   <param name="key"> 指定节点的值 </param>
 5           ///   <param name="x"> 新节点的值 </param>
 6           public   void  InsertBefore( int  key,  int  x)
 7          {
 8              Node s  =   new  Node(x);
 9              Node p  =  head;
10              Node q  =  GetNodeByVal(key);
11 
12               if  (head  ==   null   ||  q  ==   null )
13              {
14                  Console.WriteLine( " The linked list is empty or position is error! " );
15              }
16               else
17              {
18                   if  (q  ==  head)
19                  {
20                      s.Next  =  head;
21                      head  =  s;
22                  }
23                   else
24                  {
25                       while  (p.Next  !=  q)
26                      {
27                          p  =  p.Next;
28                      }
29                      s.Next  =  q;
30                      p.Next  =  s;
31                  }
32              }
33          }
复制代码

3.3 指定位置插入新结点

在链表的某个位置插入新的结点。

思路:依旧是通过先找前继结点入手,这里需要定义两个引用p(不断后移定位关键结点)和q(保存后继结点),新节点插在结点i-1和结点i之间,这里我们通过找结点i-1来找其后继结点i,这样即使i=表长+1也就是新结点插在表尾之后的话也适用,此时结点i-1就是最后一个结点,结点i就是null,然后先连后断,但如果我们通过找结点i来找其前继结点i-1的话,结点i最多只能是表尾结点,无法在表尾之后插入新结点。同上,这里如果插入位置是1的话也需要特殊处理,因为该位置没有前继结点,如果找不到该位置即i大于长度+1情况下,给出相应提示,空表和i参数不合法也需给出提示。

 
复制代码
 1           ///   <summary>
 2           ///  插入操作(指定位置插入新节点)
 3           ///   </summary>
 4           ///   <param name="i"> 指定位置序号 </param>
 5           ///   <param name="x"> 新节点的值 </param>
 6           public   void  InsertIndex( int  i,  int  x)
 7          {
 8               if  (head  ==   null   ||  i  <   1 )
 9              {
10                  Console.WriteLine( " The linked list is empty or position is error! " );
11              }
12               else
13              {
14                  Node s  =   new  Node(x);
15                  Node p  =  head;
16                  Node q  =   new  Node();
17                   int  j  =   1 ;
18                   if  (i  ==   1 )
19                  {
20                      s.Next  =  head;
21                      head  =  s;
22                  }
23                   else
24                  {
25                       while  (p  !=   null )
26                      {
27                           if  (j  ==  i  -   1 )
28                          {
29                              q  =  p.Next;
30                              s.Next  =  q;
31                              p.Next  =  s;
32                               return ;
33                          }
34                          j ++ ;
35                          p  =  p.Next;
36                      }
37                      Console.WriteLine( " The position is error! " );
38                  }
39              }
40          }
复制代码

4.删除操作

4.1 按值删除节点

给定一个关键结点的值,在该值第一次出现的位置删除掉。

思路:这里需要定义两个引用p(不断后移定位关键结点)和q(存储p的前继结点),当关键结点是第一个结点时需要特殊处理,空表或者找不到关键结点需给出提示。循环移动p若找不到该关键结点即也需提示。

 
复制代码
 1           ///   <summary>
 2           ///  删除操作(按值删除节点,只能够删第一次出现的节点)
 3           ///   </summary>
 4           ///   <param name="key"></param>
 5           public   void  DeleteValue( int  key)
 6          {
 7               if  (head  ==   null )
 8              {
 9                  Console.WriteLine( " The linked list is empty! " );
10              }
11               else
12              {
13                   if  (head.Data  ==  key)
14                  {
15                      head  =  head.Next;
16                  }
17                   else
18                  {
19                      Node p  =  head;
20                      Node q  =   new  Node();
21                       while  (p  !=   null )
22                      {
23                           if  (p.Data  ==  key)
24                          {
25                              q.Next  =  p.Next;
26                               return ;
27                          }
28                          q  =  p;
29                          p  =  p.Next;
30                      }
31                      Console.WriteLine( " The Node is not exist! " );
32                  }
33              }
34          }
复制代码

4.2按位置删除结点

给定位置序号,删除该序号对应的结点。

思路:同上,这里也需要定义两个引用p(不断后移定位关键结点)和q(存储p的前继结点),当关键结点是第一个结点时需特殊处理,空表以及位置参数不合法时需给出提示,循环移动p若找不到该位置即i>表长时也需提示。

 
复制代码
 1           ///   <summary>
 2           ///  删除操作(按位置删除节点)
 3           ///   </summary>
 4           ///   <param name="i"> 指定位置序号 </param>
 5           public   void  DeleteIndex( int  i)
 6          {
 7               if  (head  ==   null   ||  i  <   1 )
 8              {
 9                  Console.WriteLine( " The linked list is empty or position is error! " );
10              }
11               else
12              {
13                   if  (i  ==   1 )
14                  {
15                      head  =  head.Next;
16                  }
17                   else
18                  {
19                      Node p  =  head;
20                      Node q  =   new  Node();
21                       int  j  =   1 ;
22                       while  (p  !=   null )
23                      {
24                           if  (j  ==  i)
25                          {
26                              q.Next  =  p.Next;
27                               return ;
28                          }
29                          j ++ ;
30                          q  =  p;
31                          p  =  p.Next;
32                      }
33                      Console.WriteLine( " The Node is not exist! " );
34                  }
35              }
36          }
复制代码

5.反转链表

把原来的链表顺序颠倒过来

思路:需要定义两个引用p和newHead,首先p和head均指向表头,head后移,p指向的结点转向指向newHead,newHead再指向p,直到head移动到表尾,此时全部转向完成。

 
复制代码
 1           ///   <summary>
 2           ///  反转链表
 3           ///   </summary>
 4           public   void  ReverseList()
 5          {            
 6              Node p  =   null ;
 7              Node newHead  =   null ;
 8               while (head != null )
 9              {
10                  p  =  head;
11                  head  =  head.Next;
12                  p.Next  =  newHead;
13                  newHead  =  p;
14              }
15              head  =  newHead;
16          }
复制代码

6.遍历链表

从链表第一个结点开始到最后一个结点,依次输出。

思路:需要定义引用pRead不断后移逐个读取结点的值,当pRead到达表尾之后,即为null时循环结束。

 
复制代码
 1           ///   <summary>
 2           ///  遍历链表
 3           ///   </summary>
 4           public   void  ShowList()
 5          {
 6              Node pRead  =  head;
 7               while  (pRead  !=   null )
 8              {
 9                  Console.Write(pRead.Data);
10                  pRead  =  pRead.Next;
11                   if  (pRead  !=   null )
12                  {
13                      Console.Write( " , " );
14                  }
15              }
16              Console.WriteLine();
17          }
复制代码

7.求链表长度

求链表结点的个数,个数就是链表的长度。

思路:需要定义引用p不断后移对计数器累加,当p到达表尾之后,即为null时循环结束。

 
复制代码
 1           ///   <summary>
 2           ///  求链表长度
 3           ///   </summary>
 4           ///   <returns> 链表长度 </returns>
 5           public   int  GetLength()
 6          {
 7              Node p  =  head;
 8               int  len  =   0 ;
 9               while  (p  !=   null )
10              {
11                  len ++ ;
12                  p  =  p.Next;
13              }
14               return  len;
15          }
复制代码

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值