C#实现数据结构——线性表(下)
线性表链式存储结构
看了线性表的顺序存储,你肯定想线性表简是挺简单,但是我一开始怎么会知道有多少人排队?要分配多大的数组?而且插入和删除一个元素也太麻烦了,所有元素都要前移/后移,效率又低。
那怎么办呢?
这里就要用到我们的链式存储结构。
这就和我们的链条一样,一环接着一环,只要上一环知道下一环就行了。
你肯定觉得像食堂打饭那样太麻烦了,每个人都要傻傻地站在那里等,排队的位置又不够,人多了排队的地方都没有。那有没有别的方式?
也许我们可以换种方式。每个人都不需要站在那里等,也不需要知道队伍有多长前面有多少人,只需要知道你的下一个人(也就是后继)是谁就行了。比如第一个人去了打饭窗口,食堂大妈要他留下电话号码,随便去哪都行,到了打饭的时间点就电话通知他。第二个人也过来打饭,食堂大妈也让第二个人留下手机号,并且将他的手机号告诉第一个人:如果第一个人饭打完了就打电话叫第二个人来打饭。然后再将第三个人的号码告诉第二个人,由第二个人去通知第三个人,以此类推。这样每个人都不用排队了。
第一个人也就是头节点(头指针)。
当然,这在现实生活中是不可能的,那样子大妈会累死,再要碰到谁忘记通知下一个人,整个链表就断了。
初始化
每个人都不用呆在固定的地点排队,排队窗口的大小自然也就无所谓了,初始化时只需要知道第一个人的号码就可以了。当然链式结构存储的方式也和顺序不一样,不需要定义一个数组(因为没人排队了),但是需要保存一组数据及它的后继引用(指针):
public class LinkNode<T>
{
public T Node
{
get; set;
}
public LinkNode<T> NextNode
{
get; set;
}
}
然后定义头节点:
private LinkNode<T> firstNode;
获取线性表的长度
想知道有多少人排队数一下就可以了,但是现在大家都没排队,怎么知道总共有多少人在等呢?那只有找到第一个人,再通过第一个人找到第二个,以此类推,直到找到没有后继的最后一个为止:
public int Length
{
get
{
var node = firstNode;
int length = 0;
while(node != null)
{
length++;
node = node.NextNode;
}
return length;
}
}
添加元素
每来一个人打饭,就需要将新来那个人的号码告诉之前的最后一个人:
public void Add(T item)
{
if(firstNode == null)
{
firstNode = new LinkNode<T> { Node = item };
}
else
{
LinkNode<T> node = firstNode;
while(node.NextNode != null)
{
node = node.NextNode;
}
node.NextNode = new LinkNode<T> { Node = item };
}
}
删除元素
如果你女朋友(程序员竟然也能找到女朋友!)突然临时约你出去吃饭,你当然觉得食堂这么难吃的饭给程序员吃就可以了,怎么可以给女朋友吃呢?但是作为一名有素质的程序员,你自然会想办法通知前一个人你不在食堂吃饭了,然后把你下一位的号码告诉你前一个人。但是你也不知道你前一个人是谁,于是找到食堂大妈,大妈找到第一个人,然后一层层找下去,终于找到你前一个人是谁(想一想这里可以怎样优化,让你直接找到前一个人):
public void Delete(int index)
{
if(firstNode == null)
{
throw new Exception("List is empty");
}
if(index < 0)
{
throw new Exception("Location exception");
}
var node = firstNode.NextNode;
var prevNode = firstNode;
int i;
for(i = 1; i < index && node != null; i++)
{
prevNode = node;
node = node.NextNode;
}
if(i == index)
{
if(node.NextNode != null)
{
prevNode.NextNode = node.NextNode;
}
else
{
prevNode.NextNode = null;
}
}
else
{
throw new Exception("Locaion exception");
}
}
在指定位置插入元素
你女朋友约你去吃饭,于是你正打算放弃排队带她到外面吃,但是她是个勤俭持家的好女孩,认为外面吃饭太贵,还不如一起在食堂吃,省下来的钱给她买个包。正有此意又不好意思说的你一听赶紧把女朋友插在你后面。也就是你打完饭不再去通知你的下一位而是通知你女朋友,然后你女朋友再去通知他。是不是觉得链表的方式插队太方便了?神不知鬼不觉就能无痛插队,而且你女朋友通知他时,他还得说谢谢,这就是链式存储的优势:
public void Insert(T item, int index)
{
if (firstNode == null)
{
throw new Exception("List is empty");
}
if (index < 0)
{
throw new Exception("Location exception");
}
var node = firstNode.NextNode;
int i;
for (i = 1; i < index && node != null; i++)
{
node = node.NextNode;
}
if (i == index)
{
var nextNode = node.NextNode == null ? null : node.NextNode;
node.NextNode = new LinkNode<T> { Node = item, NextNode = nextNode };
}
else
{
throw new Exception("Locaion exception");
}
}
链式存储完整代码
/// <summary>
/// 线性表链式结构
/// </summary>
public class LinkList<T> : ILinearList<T>
{
private LinkNode<T> firstNode;
public int Length
{
get
{
var node = firstNode;
int length = 0;
while(node != null)
{
length++;
node = node.NextNode;
}
return length;
}
}
public void Add(T item)
{
if(firstNode == null)
{
firstNode = new LinkNode<T> { Node = item };
}
else
{
LinkNode<T> node = firstNode;
while(node.NextNode != null)
{
node = node.NextNode;
}
node.NextNode = new LinkNode<T> { Node = item };
}
}
public void Clear()
{
firstNode = null;
}
public void Delete(int index)
{
if(firstNode == null)
{
throw new Exception("List is empty");
}
if(index < 0)
{
throw new Exception("Location exception");
}
var node = firstNode.NextNode;
var prevNode = firstNode;
int i;
for(i = 1; i < index && node != null; i++)
{
prevNode = node;
node = node.NextNode;
}
if(i == index)
{
if(node.NextNode != null)
{
prevNode.NextNode = node.NextNode;
}
else
{
prevNode.NextNode = null;
}
}
else
{
throw new Exception("Locaion exception");
}
}
public T GetItem(int index)
{
if (firstNode == null)
{
throw new Exception("List is empty");
}
if (index < 0)
{
throw new Exception("Location exception");
}
var node = firstNode.NextNode;
int i;
for (i = 1; i < index && node != null; i++)
{
node = node.NextNode;
}
if (i == index)
{
return node.Node;
}
else
{
throw new Exception("Locaion exception");
}
}
public void Insert(T item, int index)
{
if (firstNode == null)
{
throw new Exception("List is empty");
}
if (index < 0)
{
throw new Exception("Location exception");
}
var node = firstNode.NextNode;
int i;
for (i = 1; i < index && node != null; i++)
{
node = node.NextNode;
}
if (i == index)
{
var nextNode = node.NextNode == null ? null : node.NextNode;
node.NextNode = new LinkNode<T> { Node = item, NextNode = nextNode };
}
else
{
throw new Exception("Locaion exception");
}
}
public bool IsEmpty()
{
return firstNode == null;
}
public int LocateItem(T t)
{
var index = 0;
var node = firstNode;
while(!node.Node.Equals(t))
{
index++;
if(node.NextNode == null)
{
return -1;
}
node = node.NextNode;
}
return index;
}
}
public class LinkNode<T>
{
public T Node
{
get; set;
}
public LinkNode<T> NextNode
{
get; set;
}
}
其它链表
之前说了,如果你想放弃排队,就要将排在你后面的人的手机号码告诉前一个人。但是由于不知道排在你前一个人是谁,所以只能去找食堂大妈,食堂大妈去找排在第一个的人,然后通过一层层找下去,才知道你前一个人是谁,这样就很麻烦,能不能让你不但知道后一个人的号码,也知道前一个人的呢?
当然可以,那就是双向链表。
双向链表就是在单链表的每个节点中,再设置一个指向其前驱节点的指针域。
用C#代码表示就是:
public class LinkNode<T>
{
public T Node
{
get; set;
}
public LinkNode<T> NextNode
{
get; set;
}
public LinkNode<T> PrevNode
{
get; set;
}
}
除了双向链表,还有循环链表、静态链表等,大家都可以自己实现。
单链表结构与顺序存储结构的优缺点
每种数据结构都不是万能的(不然还要这么多数据结构干嘛),单链表和顺序存储结构都有各自的优缺点和适用场景。
顺序存储结构的优缺点
优点:
- 无须为表示表中元素之间的逻辑关系而增加额外的存储空间(链表就需要额外存储一个指向后继的指针域)
- 可以快速地存取表中任意位置的元素(直接通过下标返回即可,链表需要循环去取)
缺点:
- 插入和删除操作需要移动大量元素
- 当线性表长度变化较大时,难以确定存储空间的容量(也可以通过达到一定长度时进行扩容的方式来改善)
- 造成存储空间的碎片(可以通过少于一定长度时进行压缩的方式来改善)
单链表的优缺点
对于插入或删除越是频繁的操作,单链表的效率优势就越是明显,但是获取元素的效率非常低。
适用场景
- 若线性表需要频繁查找,很少进行插入和删除操作,使用顺序存储结构。若需要频繁插入和删除,使用单链表结构。
- 若线性表中的元素个数变化较大或者根本不知道有多大时,最好使用单链表结构,这样可以不考虑存储空间的大小问题。而如果事先知道线性表的长度,使用顺序存储结构效率会高很多。
完整数据结构C#实现代码
C#实现数据结构——线性表(下)的更多相关文章
- [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)
[数据结构-线性表1.2] 链表与 LinkedList<T> [注:本篇文章源码内容较少,分析度较浅,请酌情选择阅读] 关键词:链表(数据结构) C#中的链表(源码) 可空类 ...
- [从今天开始修炼数据结构]线性表及其实现以及实现有Itertor的ArrayList和LinkedList
一.线性表 1,什么是线性表 线性表就是零个或多个数据元素的有限序列.线性表中的每个元素只能有零个或一个前驱元素,零个或一个后继元素.在较复杂的线性表中,一个数据元素可以由若干个数据项组成.比如牵手排 ...
- 数据结构线性表(js实现)
最近在复习数据结构的过程中,发现基本上数据结构都是用C来实现的,自己之前学习的时候也是用C去写的,由于目前对js更为熟悉一些,所以这里选择使用js去实现其中的某些算法和结构.实际上算法和语言关系不大, ...
- C#实现数据结构——线性表(上)
什么是线性表 数据结构中最常用也最简单的应该就是线性表,它是一种线性结构(废话,不是线性结构怎么会叫线性表?当然不是废话,古人公孙龙就说白马非马,现代生物学家也说鲸鱼不是鱼). 那什么是线性结构? 按 ...
- [置顶] ※数据结构※→☆线性表结构(queue)☆============循环队列 顺序存储结构(queue circular sequence)(十)
循环队列 为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量.存储在其中的队列称为循环队列(Circular Queue). ...
- [置顶] ※数据结构※→☆线性表结构(queue)☆============优先队列 链式存储结构(queue priority list)(十二)
优先队列(priority queue) 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有 ...
- [置顶] ※数据结构※→☆线性表结构(stack)☆============栈 序列表结构(stack sequence)(六)
栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线性表.栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据.栈 ...
- 数据结构-线性表的链式存储相关算法(C语言实现)
链表的简单介绍 为什么需要线性链表 当然是为了克服顺序表的缺点,在顺序表中,做插入和删除操作时,需要大量的移动元素,导致效率下降. 线性链表的分类 按照链接方式: 按照实现角度: 线性链表的创建和简单 ...
- 数据结构----线性表顺序和链式结构的使用(c)
PS:在学习数据结构之前,我相信很多博友也都学习过一些语言,比如说java,c语言,c++,web等,我们之前用的一些方法大都是封装好的,就java而言,里面使用了大量的封装好的方法,一些算法也大都写 ...
随机推荐
- 《Android开发艺术探索》读书笔记 (1) 第1章 Activity的生命周期和启动模式
第1章 Activity的生命周期和启动模式 1.1 Activity生命周期全面分析 1.1.1 典型情况下生命周期分析(1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRes ...
- 正则表达式中/i,/g,/ig,/gi,/m的区别和含义
正则表达式中/i,/g,/ig,/gi,/m的区别和含义 /i (忽略大小写)/g (全文查找出现的所有匹配字符)/m (多行查找)/gi(全文查找.忽略大小写)/ig(全文查找.忽略大小写)
- jQuery绑定事件的四种基本方式
Query中提供了四种事件监听方式,分别是bind.live.delegate.on,对应的解除监听的函数分别是unbind.die.undelegate.off. bind(type,[data], ...
- JavaScript细节整理
JavaScript是一个绝冠全球的编程语言,可用于Web开发.移动应用开发(PhoneGap.Appcelerator).服务器端开发(Node.js和Wakanda)等等.JavaScript还是 ...
- 关于myeclipse中导入的项目修改项目名使得发布到tomcat访问路径正确
我新的项目基于旧的的项目,在Myeclipse8.6里我复制了一个,但是在部署到tomcat7.0时部署过去的文件夹名还是原来的旧项目名,不是新的,但里面内容为新项目,是不是什么地方没有改.求救各位高 ...
- C# - 二叉树表达式计算
很早以前就写过双栈的表达式计算. 这次因为想深入学一下二叉树,网上都是些老掉牙的关于二叉树的基本操作. 感觉如果就学那些概念,没意思也不好记忆.于是动手写了一个表达式计算的应用例子. 这样学习印象才深 ...
- 开源的Android开发框架-------PowerFramework使用心得(二)图片异步加载ImageTask
图片异步加载.可以备注图片是否缓存.缓存状态. 1.缓存-SD卡,路径可设置 2.图片压缩 3.可加载本地和网络图片 4.url为本地视频文件可以显示缩略图 5.中文url图片地址FileNotFou ...
- leetcode修炼之路——387. First Unique Character in a String
最近公司搬家了,有两天没写了,今天闲下来了,继续开始算法之路. leetcode的题目如下: Given a string, find the first non-repeating characte ...
- Oracle解析 xml 记录一下(未完待续)
Oracle解析 xml 记录一下. SQL> desc xmlparser; PROCEDURE FREEPARSER Argument Name Type ...
- JavaScript--数组--关联(hash)数组
关联(hash)数组的原理: hash算法: 接收一个字符串,计算出一个尽量不重复的序号 不同的字符串,计算出的序号尽量不同 相同的字符串,计算出的序号一定是相同 存入数据时: 将自定义下标名称交给h ...