C#单链表
顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素, 逻辑上相邻的数据元素在物理位置上也相邻。因此,在顺序表中查找任何一个位置上的数据元素非常方便, 这是顺序存储的优点。 但是, 在对顺序表进行插入和删除时,需要通过移动数据元素来实现,影响了运行效率。线性表的另外一种存储结构——链式存储(Linked Storage), 这样的线性表叫链表(Linked List)。 链表不要求逻辑上相邻的数据元素在物理存储位置上也相邻,因此,在对链表进行插入和删除时不需要移动数据元素,但同时也失去了顺序表可随机存储的优点。
把存储据元素本身信息的域叫结点的数据域(Data Domain), 把存储与它相邻的数据元素的存储地址信息的域叫结点的引用域(Reference Domain)。如果结点的引用域只存储该结点直接后继结点的存储地址, 则该链表叫单链表(Singly Linked List)。把该引用域叫 next。把单链表结点看作是一个类,类名为 Node<T>。
单链表结点类的实现如下:
//节点类
public class Node<T>
{
private T data;//存储数据域
private Node<T> next;//存储引用域
public Node(T val, Node<T> node)
{
data = val;
next = node;
}
public Node(T val)
{
data = val;
next = null;
}
public Node(Node<T> node)
{
next = node;
}
public Node()
{
data = default(T);
next = null;
}
public T Data
{
get { return data; }
set { data = value; }
}
public Node<T> Next
{
get { return next; }
set { next = value; }
}
}
单链表的接口实现如下:
public interface IListDS<T>//单链表的接口
{
int GetLength(); //求长度
void Clear(); //清空操作
bool IsEmpty();
void Append(T item); //附加操作
void Insert(T item, int i); //插入操作
T Delete(int i); //删除操作
T GetElem(int i); //取表元
int Locate(T value); //按值查找
void Reverse();//倒置
}
单链表如图所示:
由图可知,单链表由头引用 H 唯一确定。头引用指向单链表的第一个结点,也就是把单链表第一个结点的地址放在 H 中,所以,H 是一个 Node 类型的变量。头引用为 null 表示一个空表。把单链表看作是一个类,类名叫 LinkList<T>。LinkList<T>类也实现了接口IListDS<T>。LinkList<T>类有一个字段 head,表示单链表的头引用,所以 head的类型为 Node<T>。由于链表的存储空间不是连续的,所以没有最大空间的限制,在链表中插入结点时不需要判断链表是否已满。
单链表类 LinkList<T>的实现说明如下所示:
public class LinkList<T> : IListDS<T>//单链表类
{
private Node<T> head;
public Node<T> Head
{
get { return head; }
set { head = value; }
}
public LinkList()
{
head = null;
}
public int GetLength()
{
int i = 0;
while (head != null)
{
head = head.Next;
++i;
}
return i;
}
public void Clear()
{
head = null;
}
public bool IsEmpty()
{
return head == null;
}
public void Append(T item)//在单链表的末尾添加新元素
{
Node<T> q = new Node<T>(item);
Node<T> p = new Node<T>();
if (head == null)
{
head = q;
return;
}
p = head;
while (p.Next != null)
{
p = p.Next;
}
p.Next = q;
}
//在单链表的第i个结点的位置前插入一个值为item的结点
public void Insert(T item, int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine(" List is empty or Position is error! ");
return;
}
if (i == 1)
{
Node<T> q = new Node<T>(item);
q.Next = head;
head = q;
return;
}
Node<T> p = head;
Node<T> r = new Node<T>();
int j = 0;
while (p.Next != null && j < i)
{
++j;
r = p;
p = p.Next;
}
if (i == j)
{
Node<T> q = new Node<T>(item);
q.Next = p;
r.Next = q;
}
}
//在单链表的第i个结点的位置后插入一个值为item的结点
public void InsertPost(T item, int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine(" List is empty or Position is error! ");
return;
}
Node<T> p = head;
int j = 0;
while (p.Next != null && j < i)
{
p = p.Next;
++j;
}
if (i == j)
{
Node<T> q = new Node<T>(item);
q.Next = p.Next;
p.Next = q;
}
}
public T Delete(int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine(" List is empty or Position is error! ");
return default(T);
}
Node<T> p = head;
Node<T> q = new Node<T>();
int j = 0;
while (p.Next != null && j < i)
{
q = p;
p = p.Next;
++j;
}
if (i == j)
{
q.Next = p.Next;
return p.Data;
}
else
{
Console.WriteLine("The ith node is not exist!");
return default(T);
}
}
public T GetElem(int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine(" List is empty or Position is error! ");
return default(T);
}
Node<T> p = head;
int j = 0;
while (p.Next != null && j < i)
{
p = p.Next;
++j;
}
if (i == j)
{
return p.Data;
}
else
{
Console.WriteLine("The ith node is not exist!");
return default(T);
}
}
public int Locate(T value)
{
if (IsEmpty())
{
Console.WriteLine("List is Empty!");
return -1;
}
Node<T> p = head;
int i = 0;
while (!p.Data.Equals(value) && p.Next != null)
{
p = p.Next;
++i;
}
return i;
}
public void Reverse()
{
Node<T> p = head.Next;
Node<T> q = new Node<T>();
head.Next = null;
while (p != null)
{
q = p;
p = p.Next;
q.Next = head.Next;
head.Next = q;
}
}
//单链表的建立
LinkList<int> CreateListHead()//头部插入结点建立单链表
{
LinkList<int> L = new LinkList<int>();
int d = Int32.Parse(Console.ReadLine());
while (d != -1)
{
Node<int> p = new Node<int>(d);
p.Next = L.Head;
L.Head = p;
d = Int32.Parse(Console.ReadLine());
}
return L;
}
LinkList<int> CreateListTail()//在尾部插入结点建立单链表
{
LinkList<int> L = new LinkList<int>();
Node<int> R = L.Head;
int d = Int32.Parse(Console.ReadLine());
while (d != -1)
{
Node<int> p = new Node<int>(d);
if (L.Head == null)
{
L.Head = p;
}
else
{
R.Next = p;
}
R = p;
d = Int32.Parse(Console.ReadLine());
}
if (R != null)
{
R.Next = null;
}
return L;
}
}
但是有一个问题:前面介绍的单链表允许从一个结点直接访问它的后继结点,所以, 找直接后继结点的时间复杂度是 O(1)。但是,要找某个结点的直接前驱结点,只能从表的头引用开始遍历各结点。如果某个结点的 Next 等于该结点,那么,这个结点就是该结点的直接前驱结点。也就是说,找直接前驱结点的时间复杂度是 O(n),n是单链表的长度。当然,我们也可以在结点的引用域中保存直接前驱结点的地址而不是直接后继结点的地址。这样,找直接前驱结点的时间复杂度只有 O(1),但找直接后继结点的时间复杂度是 O(n)。如果希望找直接前驱结点和直接后继结点的时间复杂度都是 O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫 prev,一个直接后继结点的地址,叫 next,这样的链表就是双向链表(Doubly Linked List)。此引出了双链表。
C#单链表的更多相关文章
- 时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法
有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和 ...
- 单链表的C++实现(采用模板类)
采用模板类实现的好处是,不用拘泥于特定的数据类型.就像活字印刷术,制定好模板,就可以批量印刷,比手抄要强多少倍! 此处不具体介绍泛型编程,还是着重叙述链表的定义和相关操作. 链表结构定义 定义单链表 ...
- Java实现单链表的各种操作
Java实现单链表的各种操作 主要内容:1.单链表的基本操作 2.删除重复数据 3.找到倒数第k个元素 4.实现链表的反转 5.从尾到头输出链表 6.找到中间节点 7.检测链表是否有环 8.在 ...
- [LeetCode] Linked List Cycle II 单链表中的环之二
Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...
- c++单链表基本功能
head_LinkNode.h /*单链表类的头文件*/#include<assert.h>#include"compare.h"typedef int status; ...
- 单链表、循环链表的JS实现
数据结构系列前言: 数据结构作为程序员的基本知识,需要我们每个人牢牢掌握.近期我也展开了对数据结构的二次学习,来弥补当年挖的坑...... 当时上课的时候也就是跟着听课,没有亲自实现任何一种数据结 ...
- C代码实现非循环单链表
C代码实现非循环单链表, 直接上代码. # include <stdio.h> # include <stdlib.h> # include <malloc.h> ...
- 分离的思想结合单链表实现级联组件:CascadeView
本文介绍自己最近做省市级联的类似的级联功能的实现思路,为了尽可能地做到职责分离跟表现与行为分离,这个功能拆分成了2个组件并用到了单链表来实现关键的级联逻辑,下一段有演示效果的gif图.虽然这是个很常见 ...
- 数据结构:单链表结构字符串(python版)添加了三个新功能
#!/urs/bin/env python # -*- coding:utf-8 -*- #异常类 class stringTypeError(TypeError): pass #节点类 class ...
- 数据结构:单链表结构字符串(python版)改进
此篇文章的replace实现了字符串类的多次匹配,但依然有些不足. 因为python字符串对象为不变对象,所以replace方法并不修改原先的字符串,而是返回修改后的字符串. 而此字符串对象时用单链表 ...
随机推荐
- 《Javascript设计模式》笔记一js的表现力
用不同方法完成同样一个任务:启动和停止动画. 1.过程式的程序设计: function startAnimation(){ ... } function stopAnimation(){ ... } ...
- Node.js的cluster模块——Web后端多进程服务
众所周知,Node.js是单线程的,一个单独的Node.js进程无法充分利用多核.Node.js从v0.6.0开始,新增cluster模块,让Node.js开发Web服务时,很方便的做到充分利用多核机 ...
- oracle基础学习
一.创建数据库用户 create user myaccount identified by mypassword;(my+account表示 账号,my+password表示 密码) 二.授予用户权限 ...
- PHP注册审核做法
1.注册页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www. ...
- 日常小测:颜色 && Hackerrank Unique_colors
题目传送门:https://www.hackerrank.com/challenges/unique-colors 感谢hzq大神找来的这道题. 考虑点分治(毕竟是路经统计),对于每一个颜色,它的贡献 ...
- Android apk集成
刚开始学习Android,对很多东西都不懂,所以以下是我做的第一件事,记录一下,也就是apk的集成: 我们集成的apk是已经签过名的第三方apk,并且需要集成到system/priv-app目录下,过 ...
- Android 组件属性
属性名称 描述 android:background 设置背景色/背景图片.可以通过以下两种方法设置背景为透明:”@android:color/transparent”和”@null”.注意TextV ...
- codeforces 484C Strange Sorting Codeforces Round #276 (Div. 1) C
思路:首先 他是对1到k 元素做一次变换,然后对2到k+1个元素做一次变化....依次做完. 如果我们对1到k个元素做完一次变换后,把整个数组循环左移一个.那么第二次还是对1 到 k个元素做和第一次一 ...
- 如何查询postgresql+openstreetmap
先行输入:psql gis \d 显示当前数据表 List of relations Schema | Name | Type | Owner --------+------------------- ...
- 关于float高度塌陷问题
和所有刚入门的菜鸟一样,我发现float有高度塌陷问题,又很偶然的发现float元素后加<img/>能消除float带来的破坏性. 后来百度了一下,大部分的float高度塌陷问题都没有提及 ...