C# 数据结构 - 单链表 双链表 环形链表

链表特点(单链表 双链表)

优点:插入和删除非常快。因为单链表只需要修改Next指向的节点,双链表只需要指向Next和Prev的节点就可以完成插入和删除操作。

缺点:当需要查找某一个节点的时候就需要一个节点一个节点去访问,这样所花的时候就比较多了。(顺序表可以弥补这缺点,但插入和删除就非常耗性能)

单链表

单链表的构成:必须要有一个链表头(head),每个节点里面有一个Next用于指向下一个节点(类似于指针)。最后一个节点的Next为null来标识链表的尾。

如下图

代码实现

 1 /* ------------  单链表 ------------- */
 2 //链表头
 3 SingleLink head = new SingleLink();
 4 SingleLink node;
 5  
 6 //添加节点
 7 node = new SingleLink(1, "刘德华");
 8 SingleLink.Add(ref head, node);
 9  
10 node = new SingleLink(2, "张学友");
11 SingleLink.Add(ref head, node);
12  
13 node = new SingleLink(3, "郭富城");
14 SingleLink.Add(ref head, node);
15  
16 node = new SingleLink(4, "黎明");
17 SingleLink.Add(ref head, node);
18  
19  
20 //删除节点
21 SingleLink.Remove(ref head, 2);
22  
23 //遍历所有节点
24 SingleLink cursor = head;
25 while (cursor.Next != null)
26 {
27     Console.WriteLine(cursor.Next.ToString());
28     cursor = cursor.Next;
29 }
30  
31  
32 /// <summary>
33 /// 单链表
34 /// </summary>
35 public class SingleLink
36 {
37     public int No { get; set; }
38  
39     public string Name { get; set; }
40  
41     //指向下一个节点(有点像指针)
42     public SingleLink Next { get; set; }
43  
44     public SingleLink() { }
45  
46     public SingleLink(int no, string name)
47     {
48         this.No = no;
49         this.Name = name;
50     }
51  
52  
53     /// <summary>
54     /// 添加
55     /// </summary>
56     /// <param name="head">链表头</param>
57     /// <param name="addNode">添加节点</param>
58     public static void Add(ref SingleLink head, SingleLink addNode)
59     {
60         SingleLink cursor = head;
61         while (cursor.Next != null)
62         {
63             cursor = cursor.Next;
64         }
65         cursor.Next = addNode;
66     }
67  
68  
69     /// <summary>
70     /// 删除
71     /// </summary>
72     /// <param name="head">链表头</param>
73     /// <param name="no">删除编号</param>
74     public static void Remove(ref SingleLink head, int no)
75     {
76         SingleLink cursor = head;
77         while (cursor.Next !=null)
78         {
79             if (cursor.Next.No == no)
80             {
81                 cursor.Next = cursor.Next.Next;
82                 return;
83             }
84             cursor = cursor.Next;
85         }
86     }
87  
88  
89     /// <summary>
90     /// 输入信息(重写ToString方法)
91     /// </summary>
92     /// <returns></returns>
93     public override string ToString()
94     {
95         return "No---> " + No + "  Name---> " + Name;
96     }
97 }

代码分析图

双链表

  双链表的构成:双链表跟单链表差不多,也是必须要有一个链表头(head),每个节点里面有一个Next,最后一个节点的Next为null来标识链表的尾。只不过双链表在每个节点上面添加一个Prev,来标识当前节点的上一个节点。

如图:

代码实现

  1 /* ------------  双链表 ------------- */
  2 //链表头
  3 DoubleLink head = new DoubleLink();
  4 DoubleLink node;
  5  
  6 //添加节点
  7 node = new DoubleLink(1,"刘德华");
  8 DoubleLink.Add(ref head, node);
  9  
 10 node = new DoubleLink(2, "张学友");
 11 DoubleLink.Add(ref head, node);
 12  
 13 node = new DoubleLink(3, "郭富城");
 14 DoubleLink.Add(ref head, node);
 15  
 16 node = new DoubleLink(4, "黎明");
 17 DoubleLink.Add(ref head, node);
 18  
 19 //删除节点
 20 DoubleLink.Remove(ref head,3);
 21  
 22 //遍历所有节点
 23 DoubleLink cursor = head;
 24 while (cursor.Next != null)
 25 {
 26     Console.WriteLine(cursor.Next.ToString());
 27     cursor = cursor.Next;
 28 }
 29  
 30  
 31 /// <summary>
 32 /// 双链表
 33 /// </summary>
 34 public class DoubleLink
 35 {
 36     public int No { get; set; }
 37  
 38     public string Name { get; set; }
 39  
 40     //下个节点
 41     public DoubleLink Prev { get; set; }
 42  
 43     //上个节点
 44     public DoubleLink Next { get; set; }
 45  
 46     public DoubleLink() { }
 47  
 48     public DoubleLink(int no, string name)
 49     {
 50         this.No = no;
 51         this.Name = name;
 52     }
 53  
 54  
 55     /// <summary>
 56     /// 添加
 57     /// </summary>
 58     /// <param name="head">链表头</param>
 59     /// <param name="addNode"></param>
 60     public static void Add(ref DoubleLink head, DoubleLink addNode)
 61     {
 62         DoubleLink cursor = head;
 63         while (cursor.Next != null)
 64         {
 65             cursor = cursor.Next;
 66         }
 67         cursor.Next = addNode;
 68         cursor.Next.Prev = cursor;
 69     }
 70  
 71  
 72     /// <summary>
 73     /// 删除
 74     /// </summary>
 75     /// <param name="head">链表头</param>
 76     /// <param name="no">删除编号</param>
 77     public static void Remove(ref DoubleLink head, int no)
 78     {
 79         DoubleLink cursor = head.Next;
 80         while (cursor != null)
 81         {
 82             if (cursor.No == no)
 83             {
 84                 //防止是删除最后一个
 85                 if (cursor.Next != null)
 86                 {
 87                     cursor.Next.Prev = cursor.Prev;
 88                 }
 89                 cursor.Prev.Next = cursor.Next;
 90                 return;
 91             }
 92             cursor = cursor.Next;
 93         }
 94     }
 95  
 96  
 97     /// <summary>
 98     /// 输入信息(重写ToString方法)
 99     /// </summary>
100     /// <returns></returns>
101     public override string ToString()
102     {
103         return "No---> " + No + "  Name---> " + Name;
104     }
105 }

代码分析图

环形链表

  环形链表是在单链表基础之上把尾指向头就构成了一个环形的链表了。也就是单链表最后一个节点的Next指Head就可以了。

 如图:

  

代码实现

  1 //环形链表
  2 LoopLink head = new LoopLink();
  3 LoopLink node;
  4
  5
  6 //添加节点
  7 node = new LoopLink(1, "刘德华");
  8 LoopLink.Add(ref head, node);
  9
 10 node = new LoopLink(2, "张学友");
 11 LoopLink.Add(ref head, node);
 12
 13 node = new LoopLink(3, "郭富城");
 14 LoopLink.Add(ref head, node);
 15
 16 node = new LoopLink(4, "黎明");
 17 LoopLink.Add(ref head, node);
 18
 19 LoopLink.Remove(ref head, 2);
 20
 21 //遍历所有节点(遍历需要拿到它的头或者尾来标识链表的结束位置,我是拿到头来遍历的)
 22 LoopLink cursor = head;
 23 LoopLink first = head;
 24 //首先输出头的信息
 25 Console.WriteLine(head);
 26 //遍历所有节点,如果遍历到头节点就退出循环,链表打印完成
 27 while (!Object.ReferenceEquals(cursor.Next, first))
 28 {
 29     Console.WriteLine(cursor.Next.ToString());
 30     cursor = cursor.Next;
 31 }
 32
 33
 34
 35 /// <summary>
 36 /// 环形链表
 37 /// </summary>
 38 class LoopLink
 39 {
 40
 41     public int No { get; set; }
 42
 43     public string Name { get; set; }
 44
 45     public LoopLink Next { get; set; }
 46
 47     public LoopLink() { }
 48
 49     public LoopLink(int no, string name)
 50     {
 51         this.No = no;
 52         this.Name = name;
 53     }
 54
 55     public static LoopLink Cursor { get; set; }
 56
 57     /// <summary>
 58     /// 添加
 59     /// </summary>
 60     /// <param name="head">链表头</param>
 61     /// <param name="addNode">所要添加节点</param>
 62     public static void Add(ref LoopLink head, LoopLink addNode)
 63     {
 64         if (head.Next == null)
 65         {
 66             head = addNode;
 67             head.Next = addNode;
 68             Cursor = head;
 69         }
 70         else
 71         {
 72             Cursor.Next = addNode;
 73             addNode.Next = head;
 74             Cursor = Cursor.Next;
 75         }
 76     }
 77
 78
 79     /// <summary>
 80     /// 删除节点
 81     /// </summary>
 82     /// <param name="head">链表头</param>
 83     /// <param name="no">所要删除编号</param>
 84     public static void Remove(ref LoopLink head, int no)
 85     {
 86         LoopLink cur = head;
 87
 88         while (true)
 89         {
 90             if (cur.Next.No == no)
 91             {
 92                 cur.Next = cur.Next.Next;
 93                 return;
 94             }
 95             cur = cur.Next;
 96         }
 97     }
 98
 99
100     /// <summary>
101     /// 输入信息(重写ToString方法)
102     /// </summary>
103     /// <returns></returns>
104     public override string ToString()
105     {
106         return "No---> " + No + "  Name---> " + Name;
107     }
108 }

扩展训练(面试题)

  题目:有一颗炸弹,有N个人围成一个圆形,从第K个人开始数数,数M就退出圆。最后算出留下来要被炸死是第几个人。请用代码实现。

  代码如下:

  1 //N个人
  2 int n = 5;
  3
  4 //第K个人开始数数
  5 int k = 3;
  6
  7 //数到M就退出
  8 int m = 4;
  9
 10 Game head = new Game();
 11
 12 //构建一个由N个人组成的圆形
 13 Game.Add(ref head, n);
 14 //查找谁会被炸死
 15 Game.Bomb(ref head, k, m);
 16
 17
 18
 19 /// <summary>
 20 /// 游戏类
 21 /// </summary>
 22 public class Game
 23 {
 24     public int No { get; set; }
 25
 26     public Game Next { get; set; }
 27
 28     public Game() { }
 29
 30     public Game(int no)
 31     {
 32         this.No = no;
 33     }
 34
 35
 36     public static void Add(ref Game head, int n)
 37     {
 38         Game cursor = null;
 39         for (int i = 0; i < n; i++)
 40         {
 41             Game temp = new Game(i+1);
 42             if (i == 0)
 43             {
 44                 head.Next = temp;
 45                 head = temp;
 46                 cursor = head;
 47             }
 48             else
 49             {
 50                 cursor.Next = temp;
 51                 temp.Next = head;
 52                 cursor = cursor.Next;
 53             }
 54         }
 55     }
 56
 57
 58     /// <summary>
 59     /// 查找谁会被炸死
 60     /// </summary>
 61     /// <param name="head">链表头</param>
 62     /// <param name="k">开始数数的人</param>
 63     /// <param name="m">数几就退出圆圈</param>
 64     public static void Bomb(ref Game head,int k, int m)
 65     {
 66         Game tail = head;
 67
 68         //拿到最后一个节点
 69         while (!Object.ReferenceEquals(tail.Next,head))
 70         {
 71             tail = tail.Next;
 72         }
 73
 74
 75         //从第K个人开始数数
 76         for (int i = 0; i < k; i++)
 77         {
 78             head = head.Next;
 79             tail = tail.Next;
 80         }
 81
 82
 83         //当头跟尾是同一个节点说明找到最终的节点
 84         int mark = 1;
 85         while (!object.ReferenceEquals(tail,head))
 86         {
 87             for (int i = 0; i < m-1; i++)
 88             {
 89                 head = head.Next;
 90                 tail = tail.Next;
 91             }
 92
 93             Console.WriteLine(@"第"+mark+@"个安全退出人的编号:"+head.No);
 94
 95             head = head.Next;
 96             tail.Next = head;
 97             mark++;
 98         }
 99
100         Console.WriteLine(@"最后被炸死人的编号:" + head.No);
101     }
102 }

  总结:

    1.扩展训练题其实考的就是环形链表,这个题目跟约瑟夫问题是一样的(实现思路是一样的)

    2.“程序=算法+数据结构”,但是在我们真正开发的时候算法和数据结构用到很少,一般比较大型项目或者复杂度比较高项目和游戏里面可能才会用到这些,但是我们还是要了解,说不定面试考到了呢?(答对了会加分的哦!^_^)

    3.链表什么时候会用到呢?比如说象棋里面有悔棋,我们就可以用双向链表来实现,这样就可以悔到上一步也可以下一步了,还有很多应用场景。而单链表的话像LinkedList就是用单链表实现的(但是LinkedList被List代替,不推荐用LinkedList,推荐用List.自己查一下为什么?)

  

  (本人学识有限,文章如有误,愿见谅。希望能指出来,以免误人子弟了。^_^)

时间: 2024-10-10 18:09:40

C# 数据结构 - 单链表 双链表 环形链表的相关文章

环形链表--链表

题目 给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. 示例 1: 输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点.  示例 2: 输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点.  示例 3: 输入:head = [1], po

数据结构单链表,双链表,数组的底层实现原理

单链表: 链表是动态分配内存在内存中不连续,单链表只有一个向下的指针,指向下一个节点,单链表的定位时间复杂度是O(n),插入删除的时间复杂度是O(1) 双链表: 链表是动态分配内容在内存中不连续,单双链表一致,双链表有两个指针prov,next ,prov指向上一个节点,next指向下一个节点,理论上同样的数据量双向链表的查询速度比单链表快,双向链表可以使用二分查找法,最多查找一半的元素就可得到目标,单链表需要遍历整个链表对象. 数组: 数组静态分配内存,在内存中连续 数组的优点 随机访问性强(

php实现单,双向链表,环形链表解决约瑟夫问题

传智播客PHP学院 韩顺平 PHP程序员玩转算法第一季  http://php.itcast.cn 聊天篇: 数学对我们编程来说,重不重要? 看你站在什么样的层次来说. 如果你应用程序开发,对数学要求不高 但是,如果你开发系统软件,比如(搜索/识别软件[图像,语言识别]/操作系统...)对数学高 建模.大量数学模型. 老师啊啊.我是学C++的.麻烦,谈哈对QT和MFC的看法嘛.前景什么的, 记住 : 打好基础,大有可为! 初中毕业能去传智学习吗? 学习It, 不管是java ,php ,c#,对

数据结构学习之双链表基本操作

数据结构学习之双链表基本操作 0x1 前言 今天实验课,学习了下双链表的写法,这里记录下. 0x2 正文 题目要求如下: 本实验的双链链表元素的类型为char,完成如下实验要求: (1)初始化单链表h (2)采用尾插法依次插入a.b.c.d.e (3)输出单链表h (4)输出单链表h的长度 (5)判断单链表h是否为空 (6)输出单链表h的第3个元素 (7)输出元素a的逻辑位置 (8)在第4个元素位置上插入元素f (9)输出单链表h (10)删除单链表h的第3个元素 (11)输出单链表h (12)

python实现数据结构单链表

#python实现数据结构单链表 # -*- coding: utf-8 -*- class Node(object): """节点""" def __init__(self, elem): self.elem = elem self.next = None # 节点一开始初始化的时候并不知道下一个元素的地址,所以先设置为空 class SingLinkList(object): """单链表""

Java数据结构之单向环形链表(解决Josephu约瑟夫环问题)

1.Josephu(约瑟夫.约瑟夫环)问题: 设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列. 提示: 用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法

数据结构-单链表-结构体定义

单链表,用于存储逻辑关系为 "一对一" 的数据,与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的. 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻. 例如{1,2,3}: 线性表的链式表示又称为非顺序映像或链式映像. 各结点由两个域组成: 数据域:存储元素数值数据 指针域:存储直接后继结点的存储位置 头指针是指向链表中第一个结点的指针 首元结点是指链表中存储第一个数据元素a1的结点 头结点是在链表的首元结点之前

数据结构&gt;&gt;线性表【注意】--&gt;链表求A-B(原A与B都递增,求完的A-B不改变A原来的顺序)

/*关于链表的题目 * A.B是两个递增有序的单链表,元素个数分别是m和n,求 * 集合A-B,并将结果保存在A中,且仍然保持递增有序. * converge_ab */ #include <iostream.h> using namespace std; typedef struct lnode{ int data; struct lnode * next; }lnode; int main(){ lnode * create_chain(int num,int interval,int s

数据结构实验之链表四:有序链表的归并

数据结构实验之链表四:有序链表的归并 Time Limit: 1000MS Memory limit: 65536K 题目描述 分别输入两个有序的整数序列(分别包含M和N个数据),建立两个有序的单链表,将这两个有序单链表合并成为一个大的有序单链表,并依次输出合并后的单链表数据. 输入 第一行输入M与N的值: 第二行依次输入M个有序的整数: 第三行依次输入N个有序的整数. 输出 输出合并后的单链表所包含的M+N个有序的整数. 示例输入 6 5 1 23 26 45 66 99 14 21 28 5