链表是有序列表,它在内存中是存储如下:
上图的表格来模拟链表的结构,其中每一行为一个节点(以第一行举例110,a2,180)。
单链表(带头节点)逻辑结构示意图如下:
最后一个节点的‘next域’为空。
使用带head头的单项链表实现,对数据的增删改查操作。
代码实现思路:
添加(创建)
遍历:
数据结构定义
public class DataNode
{
public int Id { get; set; }
public string Name { get; set; }
public string NikeName { get; set; }
public DataNode Node { get; set; }
public DataNode(int id, string name, string nikeName)
{
Id = id;
Name = name;
NikeName = nikeName;
}
public override string ToString()
{
return $"DataNode[{Id},{Name},{NikeName}]";
}
}
public class SingleLinkedList
{
/*
* 定义一个头节点,保证不要改动它因为被修改之后就找不到链表的最顶端。
* 先初始化一个头节点,头节点不要动,不存放具体的数据
*/
private DataNode head = new DataNode(0,"","");
/*
* 当不考虑编号的顺序时
* 1.找到当前链表的最后节点
* 2.将最后这个节点的next域指向新的节点即可
*/
public void Add(DataNode node)
{
//因为head节点不能改动,因此我们需要一个辅助变量辅助遍历temp
DataNode temp = head;
//遍历链表,找到最后
while (true)
{
//找到链表的最后
if (temp.NextNode == null)
{
break;
}
//如果没有找到则后移
temp = temp.NextNode;
}
//当推出while循环时,temp就指向了链表的最后
//将最后这个节点的next指向新的节点
temp.NextNode = node;
}
//显示链表(遍历)
public void List()
{
if (head.NextNode == null)
{
Console.WriteLine("链表为空");
return;
}
//因为头节点不能改动,因此我们需要一个辅助变量来遍历
DataNode temp = head.NextNode;
while (true)
{
//判断是否到链表最后
if (temp == null)
{
break;
}
//输出节点信息
Console.WriteLine(temp);
//将next后移,一定记住需要后移。不然就是死循环
temp = temp.NextNode;
}
}
}
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
list.Add(node1);
list.Add(node2);
list.Add(node3);
list.Add(node4);
list.List();
}
}
如果当我们把list.add的添加顺序修改一下。会发现:
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);
list.List();
}
}
目前这种写法是按照添加顺序来显示的,add时并没有考虑编号。接下来将继续演示:
需要按照变化的顺序添加
代码如下:
/*
* 第二种方式在添加节点时,根据排名将节点插入到指定位置
* 如果有这个排名,则添加失败并提示
*/
public void AddNodeByOrder(DataNode node)
{
//因为头节点不能东,因此我们仍然通过一个辅助变量来帮助找到添加的位置
//因为单链表,因此我们找的tmep是位于添加位置的前一个节点,否则插入不了
DataNode temp = head;
bool flag = false;//标识添加的编号是否存在,默认为false
while (true)
{
if (temp.NextNode == null)
{
//说明temp已经在链表的最后
break;
}
if (temp.NextNode.Id > node.Id)
{
//位置找到,就在temp的后面插入
break;
}
else if(temp.NextNode.Id == node.Id)
{
//说明希望添加的datanode的编号已经存在
flag = true;//说明编号存在
break;
}
//后移,遍历当前的链表
temp = temp.NextNode;
}
//判断flag的值
if (flag)
{
//不能添加,说明编号已经存在
Console.WriteLine($"准备插入的节点编号{node.Id}已经存在");
}
else
{
//插入到链表中,temp的后面
node.NextNode = temp.NextNode;
temp.NextNode = node;
}
}
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
/* list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);*/
list.AddNodeByOrder(node1);
list.AddNodeByOrder(node4);
list.AddNodeByOrder(node2);
list.AddNodeByOrder(node3);
list.List();
}
}
然后我们再测试一下添加重复节点的效果:
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
/* list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);*/
list.AddNodeByOrder(node1);
list.AddNodeByOrder(node4);
list.AddNodeByOrder(node2);
list.AddNodeByOrder(node3);
list.AddNodeByOrder(node3);
list.List();
}
}
/*
* 修改节点的信息,根据编号来修改,即编号不能改
* 说明:
* 1.根据NewDataNode的id来修改即可
*/
public void Update(DataNode node)
{
//判断链表是否空
if (head.NextNode == null)
{
Console.WriteLine("链表为空");
return;
}
//找到需要修改的节点,根据id
//先顶一个一个辅助变量
DataNode temp = head.NextNode;
bool flag = false;//表示是否找到该节点
while (true)
{
if (temp == null)
{
break;//已经遍历完链表
}
if (temp.Id == node.Id)
{
//找到了
flag = true;
break;
}
temp = temp.NextNode;
}
//根据flag判断是否找到要修改的节点
if (flag)
{
temp.Name = node.Name;
temp.NikeName = node.NikeName;
}
else
{
//没有找到
Console.WriteLine($"没有找到编号的节点,不能修改{node.Id}");
}
}
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
/* list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);*/
list.AddNodeByOrder(node1);
list.AddNodeByOrder(node4);
list.AddNodeByOrder(node2);
list.AddNodeByOrder(node3);
list.List();
Console.WriteLine();
//修改节点
var newNode = new DataNode(2, "Juster", "Justerter!!!");
list.Update(newNode);
list.List();
}
}
从单链表删除一个节点的思路
删除1号
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
/* list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);*/
list.AddNodeByOrder(node1);
list.AddNodeByOrder(node4);
list.AddNodeByOrder(node2);
list.AddNodeByOrder(node3);
list.List();
Console.WriteLine();
//修改节点
//var newNode = new DataNode(2, "Juster", "Justerter!!!");
//list.Update(newNode);
//删除节点
list.Delete(1);
list.List();
}
}
全部删除
class Program
{
static void Main(string[] args)
{
var node1 = new DataNode(1,"法外狂徒张三","三哥");
var node2 = new DataNode(2, "隔壁李四", "四哥");
var node3 = new DataNode(3, "楼下王五", "五哥");
var node4 = new DataNode(4, "同学老六", "六哥");
var list = new SingleLinkedList();
/* list.Add(node1);
list.Add(node4);
list.Add(node2);
list.Add(node3);*/
list.AddNodeByOrder(node1);
list.AddNodeByOrder(node4);
list.AddNodeByOrder(node2);
list.AddNodeByOrder(node3);
list.List();
Console.WriteLine();
//修改节点
//var newNode = new DataNode(2, "Juster", "Justerter!!!");
//list.Update(newNode);
//删除节点
list.Delete(1);
list.Delete(4);
list.Delete(2);
list.Delete(3);
list.List();
}
}