线性表
来源:互联网 发布:怪物猎人x中文数据库 编辑:程序博客网 时间:2024/06/01 10:24
数据间存在三种基本关系:
<1> 线性关系。
<2> 树形关系。
<3> 网状关系。
一: 线性表
1 概念:
线性表也就是关系户中最简单的一种关系,一对一。
如:学生学号的集合就是一个线性表。
2 特征:
① 有且只有一个“首元素“。
② 有且只有一个“末元素”。
③ 除“末元素”外,其余元素均有唯一的后继元素。
④ 除“首元素”外,其余元素均有唯一的前驱元素。
3 存储划分:
① 如果把线性表用“顺序存储”,那么就是“顺序表”。
② 如果把线性表用“链式存储”,那么就是“链表”。
4 常用操作:添加,删除,插入,查找,遍历,统计。
今天主要就说说“线性表”的“顺序存储”。
那么下面就简单的浅析一下这个操作的原理和复杂度。
<1> 初始化顺序表:
这个操作其实还是蛮简单的,设置length=0,也就是O(1)的时间。
<2> 求顺序表长度:
这个不解释,O(1)的时间。
<3> 添加节点:
因为是顺序表,所以添加的节点直接会放到数组的末尾,时间也是O(1)的。
<4> 插入节点:
这个还是有点小麻烦的,主要也就是说分两种情况:
①:当插入节点在数组的最后,那么这个“插入”其实就是”添加“操作,时间当然是O(1)。
②:当插入节点在数组的开头,那就悲催了,被插入节点的后续元素都要向后移动一位,
也就让整个数组一阵痉挛,效率低下可想而知,时间复杂度退化为O(n)。
<5> 删除节点:
这个跟“插入”的道理是一样的,也要分两个情况,
①:当删除的元素在数组的最后,不用移位,谢天谢地,时间为O(1)。
②: 当删除的元素在数组的开头,删除节点处的元素都要统统向前移位,同样也是一阵痉挛,
时间复杂度也退化为O(n)。
<6> 按序号查找节点:
大家都知道,顺序表的存储地址是连续的,所以第N个元素地址公式为:(N-1)X 数据存储长度。
哈哈,这就是顺序表得瑟的地方,查找的时间复杂度为O(1)。
<7> 按关键字查找:
嗯,这个在日常开发中用的最多的,那么就避免不了将key的值在我们的list中查找,前期也说过,
最快的查找是O(1),当然他是用空间来换取时间的,最慢的查找是O(n),那么这里我们就一个for
循环搞定,时间复杂度为O(n)。
说了这么多,目的就是预先评估算法的执行效率,给我们带来一手的参考资料,做到真正的运筹帷幄,决胜千里之外。
这也是我们学习算法的目的,到时候不会让我们说tnd,程序歇菜了,我也歇菜了。
好,现在是上代码时间。
运行结果:
通过实验,大家也知道,如果我每次向
顺序表的头部插入元素,都会引起痉挛,效率比较低下,第二点我们用顺序存储时,容
易受到长度的限制,反之就会造成空间资源的浪费。
二:链表
对于顺序表存在的若干问题,链表都给出了相应的解决方案。
1. 概念:其实链表的“每个节点”都包含一个”数据域“和”指针域“。
”数据域“中包含当前的数据。
”指针域“中包含下一个节点的指针。
”头指针”也就是head,指向头结点数据。
“末节点“作为单向链表,因为是最后一个节点,通常设置指针域为null。
代码段如下:
1 #region 链表节点的数据结构 2 /// <summary> 3 /// 链表节点的数据结构 4 /// </summary> 5 public class Node<T> 6 { 7/// <summary> 8 /// 节点的数据域 9 /// </summary>10 public T data;11 12 /// <summary>13 /// 节点的指针域14 /// </summary>15 public Node<T> next;16 }17 #endregion
2.常用操作:
链表的常用操作一般有:
①添加节点到链接尾,②添加节点到链表头,③插入节点。
④删除节点,⑤按关键字查找节点,⑥取链表长度。
<1> 添加节点到链接尾:
前面已经说过,链表是采用指针来指向下一个元素,所以说要想找到链表最后一个节点,
必须从头指针开始一步一步向后找,少不了一个for循环,所以时间复杂度为O(N)。
代码段如下:
1 #region 将节点添加到链表的末尾 2 /// <summary> 3 /// 将节点添加到链表的末尾 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="head"></param> 7 /// <param name="data"></param> 8 /// <returns></returns> 9 public Node<T> ChainListAddEnd<T>(Node<T> head, T data)10 {11 Node<T> node = new Node<T>();12 13 node.data = data;14 node.next = null;15 16 ///说明是一个空链表17 if (head == null)18 {19 head = node;20 return head;21 }22 23 //获取当前链表的最后一个节点24 ChainListGetLast(head).next = node;25 26 return head;27 }28 #endregion29 #region 得到当前链表的最后一个节点30 /// <summary>31 /// 得到当前链表的最后一个节点32 /// </summary>33 /// <typeparam name="T"></typeparam>34 /// <param name="head"></param>35 /// <returns></returns>36 public Node<T> ChainListGetLast<T>(Node<T> head)37 {38 if (head.next == null)39 return head;40 return ChainListGetLast(head.next);41 }42 #endregion
<2> 添加节点到链表头:
大家现在都知道,链表是采用指针指向的,要想将元素插入链表头,其实还是很简单的,
思想就是:① 将head的next指针给新增节点的next。②将整个新增节点给head的next。
所以可以看出,此种添加的时间复杂度为O(1)。
效果图:
代码段如下:
1#region 将节点添加到链表的开头 2 /// <summary> 3 /// 将节点添加到链表的开头 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="chainList"></param> 7 /// <param name="data"></param> 8 /// <returns></returns> 9 public Node<T> ChainListAddFirst<T>(Node<T> head, T data)10 {11 Node<T> node = new Node<T>();12 13 node.data = data;14 node.next = head;15 16 head = node;17 18 return head;19 20 }21 #endregion
<3> 插入节点:
其实这个思想跟插入到”首节点“是一个模式,不过多了一步就是要找到当前节点的操作。然后找到
这个节点的花费是O(N)。上图上代码,大家一看就明白。
效果图:
代码段:
1 #region 将节点插入到指定位置 2 /// <summary> 3 /// 将节点插入到指定位置 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="head"></param> 7 /// <param name="currentNode"></param> 8 /// <param name="data"></param> 9 /// <returns></returns>10 public Node<T> ChainListInsert<T, W>(Node<T> head, string key, Func<T, W> where, T data) where W : IComparable11 {12 if (head == null)13 return null;14 15 if (where(head.data).CompareTo(key) == 0)16 {17 Node<T> node = new Node<T>();18 19 node.data = data;20 21 node.next = head.next;22 23 head.next = node;24 }25 26 ChainListInsert(head.next, key, where, data);27 28 return head;29 }30 #endregion
<4> 删除节点:
这个也比较简单,不解释,图跟代码更具有说服力,口头表达反而让人一头雾水。
当然时间复杂度就为O(N),N是来自于查找到要删除的节点。
效果图:
代码段:
1 #region 将指定关键字的节点删除 2 /// <summary> 3 /// 将指定关键字的节点删除 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <typeparam name="W"></typeparam> 7 /// <param name="head"></param> 8 /// <param name="key"></param> 9 /// <param name="where"></param>10 /// <param name="data"></param>11 /// <returns></returns>12 public Node<T> ChainListDelete<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable13 {14 if (head == null)15 return null;16 17 //这是针对只有一个节点的解决方案18 if (where(head.data).CompareTo(key) == 0)19 {20 if (head.next != null)21 head = head.next;22 else23 return head = null;24 }25 else26 {27 //判断一下此节点是否是要删除的节点的前一节点28 while (head.next != null && where(head.next.data).CompareTo(key) == 0)29 {30 //将删除节点的next域指向前一节点31 head.next = head.next.next;32 }33 }34 35 ChainListDelete(head.next, key, where);36 37 return head;38 }39 #endregion
<5> 按关键字查找节点:
这个思想已经包含到“插入节点”和“删除节点”的具体运用中的,其时间复杂度为O(N)。
代码段:
1 #region 通过关键字查找指定的节点 2 /// <summary> 3 /// 通过关键字查找指定的节点 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <typeparam name="W"></typeparam> 7 /// <param name="head"></param> 8 /// <param name="key"></param> 9 /// <param name="where"></param>10 /// <returns></returns>11 public Node<T> ChainListFindByKey<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable12 {13 if (head == null)14 return null;15 16 if (where(head.data).CompareTo(key) == 0)17 return head;18 19 return ChainListFindByKey<T, W>(head.next, key, where);20 }21 #endregion
<6> 取链表长度:
在单链表的操作中,取链表长度还是比较纠结的,因为他不像顺序表那样是在内存中连续存储的,
因此我们就纠结的遍历一下链表的总长度。时间复杂度为O(N)。
代码段:
1 #region 获取链表的长度 2 /// <summary> 3 ///// 获取链表的长度 4 /// </summary> 5 /// <typeparam name="T"></typeparam> 6 /// <param name="head"></param> 7 /// <returns></returns> 8 public int ChanListLength<T>(Node<T> head) 9 {10 int count = 0;11 12 while (head != null)13 {14 ++count;15 head = head.next;16 }17 18 return count;19 }20 #endregion
好了,最后上一下总的运行代码:
- <pre name="code" class="java">using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace ChainList
- {
- class Program
- {
- static void Main(string[] args)
- {
- ChainList chainList = new ChainList();
- Node<Student> node = null;
- Console.WriteLine("将三条数据添加到链表的尾部:\n");
- //将数据添加到链表的尾部
- node = chainList.ChainListAddEnd(node, new Student() { ID = 2, Name = "hxc520", Age = 23 });
- node = chainList.ChainListAddEnd(node, new Student() { ID = 3, Name = "博客园", Age = 33 });
- node = chainList.ChainListAddEnd(node, new Student() { ID = 5, Name = "一线码农", Age = 23 });
- Dispaly(node);
- Console.WriteLine("将ID=1的数据插入到链表开头:\n");
- //将ID=1的数据插入到链表开头
- node = chainList.ChainListAddFirst(node, new Student() { ID = 1, Name = "i can fly", Age = 23 });
- Dispaly(node);
- Console.WriteLine("查找Name=“一线码农”的节点\n");
- //查找Name=“一线码农”的节点
- var result = chainList.ChainListFindByKey(node, "一线码农", i => i.Name);
- DisplaySingle(node);
- Console.WriteLine("将”ID=4“的实体插入到“博客园”这个节点的之后\n");
- //将”ID=4“的实体插入到"博客园"这个节点的之后
- node = chainList.ChainListInsert(node, "博客园", i => i.Name, new Student() { ID = 4, Name = "51cto", Age = 30 });
- Dispaly(node);
- Console.WriteLine("删除Name=‘51cto‘的节点数据\n");
- //删除Name=‘51cto‘的节点数据
- node = chainList.ChainListDelete(node, "51cto", i => i.Name);
- Dispaly(node);
- Console.WriteLine("获取链表的个数:" + chainList.ChanListLength(node));
- }
- //输出数据
- public static void Dispaly(Node<Student> head)
- {
- Console.WriteLine("******************* 链表数据如下 *******************");
- var tempNode = head;
- while (tempNode != null)
- {
- Console.WriteLine("ID:" + tempNode.data.ID + ", Name:" + tempNode.data.Name + ",Age:" + tempNode.data.Age);
- tempNode = tempNode.next;
- }
- Console.WriteLine("******************* 链表数据展示完毕 *******************\n");
- }
- //展示当前节点数据
- public static void DisplaySingle(Node<Student> head)
- {
- if (head != null)
- Console.WriteLine("ID:" + head.data.ID + ", Name:" + head.data.Name + ",Age:" + head.data.Age);
- else
- Console.WriteLine("未查找到数据!");
- }
- }
- #region 学生数据实体
- /// <summary>
- /// 学生数据实体
- /// </summary>
- public class Student
- {
- public int ID { get; set; }
- public string Name { get; set; }
- public int Age { get; set; }
- }
- #endregion
- #region 链表节点的数据结构
- /// <summary>
- /// 链表节点的数据结构
- /// </summary>
- public class Node<T>
- {
- /// <summary>
- /// 节点的数据域
- /// </summary>
- public T data;
- /// <summary>
- /// 节点的指针域
- /// </summary>
- public Node<T> next;
- }
- #endregion
- #region 链表的相关操作
- /// <summary>
- /// 链表的相关操作
- /// </summary>
- public class ChainList
- {
- #region 将节点添加到链表的末尾
- /// <summary>
- /// 将节点添加到链表的末尾
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="head"></param>
- /// <param name="data"></param>
- /// <returns></returns>
- public Node<T> ChainListAddEnd<T>(Node<T> head, T data)
- {
- Node<T> node = new Node<T>();
- node.data = data;
- node.next = null;
- ///说明是一个空链表
- if (head == null)
- {
- head = node;
- return head;
- }
- //获取当前链表的最后一个节点
- ChainListGetLast(head).next = node;
- return head;
- }
- #endregion
- #region 将节点添加到链表的开头
- /// <summary>
- /// 将节点添加到链表的开头
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="chainList"></param>
- /// <param name="data"></param>
- /// <returns></returns>
- public Node<T> ChainListAddFirst<T>(Node<T> head, T data)
- {
- Node<T> node = new Node<T>();
- node.data = data;
- node.next = head;
- head = node;
- return head;
- }
- #endregion
- #region 将节点插入到指定位置
- /// <summary>
- /// 将节点插入到指定位置
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="head"></param>
- /// <param name="currentNode"></param>
- /// <param name="data"></param>
- /// <returns></returns>
- public Node<T> ChainListInsert<T, W>(Node<T> head, string key, Func<T, W> where, T data) where W : IComparable
- {
- if (head == null)
- return null;
- if (where(head.data).CompareTo(key) == 0)
- {
- Node<T> node = new Node<T>();
- node.data = data;
- node.next = head.next;
- head.next = node;
- }
- ChainListInsert(head.next, key, where, data);
- return head;
- }
- #endregion
- #region 将指定关键字的节点删除
- /// <summary>
- /// 将指定关键字的节点删除
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="W"></typeparam>
- /// <param name="head"></param>
- /// <param name="key"></param>
- /// <param name="where"></param>
- /// <param name="data"></param>
- /// <returns></returns>
- public Node<T> ChainListDelete<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable
- {
- if (head == null)
- return null;
- //这是针对只有一个节点的解决方案
- if (where(head.data).CompareTo(key) == 0)
- {
- if (head.next != null)
- head = head.next;
- else
- return head = null;
- }
- else
- {
- //判断一下此节点是否是要删除的节点的前一节点
- if (head.next != null && where(head.next.data).CompareTo(key) == 0)
- {
- //将删除节点的next域指向前一节点
- head.next = head.next.next;
- }
- }
- ChainListDelete(head.next, key, where);
- return head;
- }
- #endregion
- #region 通过关键字查找指定的节点
- /// <summary>
- /// 通过关键字查找指定的节点
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="W"></typeparam>
- /// <param name="head"></param>
- /// <param name="key"></param>
- /// <param name="where"></param>
- /// <returns></returns>
- public Node<T> ChainListFindByKey<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable
- {
- if (head == null)
- return null;
- if (where(head.data).CompareTo(key) == 0)
- return head;
- return ChainListFindByKey<T, W>(head.next, key, where);
- }
- #endregion
- #region 获取链表的长度
- /// <summary>
- ///// 获取链表的长度
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="head"></param>
- /// <returns></returns>
- public int ChanListLength<T>(Node<T> head)
- {
- int count = 0;
- while (head != null)
- {
- ++count;
- head = head.next;
- }
- return count;
- }
- #endregion
- #region 得到当前链表的最后一个节点
- /// <summary>
- /// 得到当前链表的最后一个节点
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="head"></param>
- /// <returns></returns>
- public Node<T> ChainListGetLast<T>(Node<T> head)
- {
- if (head.next == null)
- return head;
- return ChainListGetLast(head.next);
- }
- #endregion
- }
- #endregion
- }
运行结果:
当然,单链表操作中有很多是O(N)的操作,这给我们带来了尴尬的局面,所以就有了很多的
优化方案,比如:双向链表,循环链表。静态链表等等,这些希望大家在懂得单链表的情况下
待深一步的研究
- 线性表--线性存储
- 线性表 线性结构
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- 线性表
- POJ 2305:Basic remains 进制转换
- javascript循环性能优化的几种方法
- ubuntu中设定ibus自启动
- *第七周*数据结构实践项目二【建设链式队列算法库】
- 知道什么时候该停下来!
- 线性表
- 批量的pdf怎么转换成txt格式的文件
- 优化shader加载时间
- 欢迎使用CSDN-markdown编辑器
- swift2.0 常用标注 //MARK-: 替换 #pragma mark - #warning
- 设计模式之单例模式
- 队列
- rk3288 lvds TF卡修改屏参
- 东北大学地理位置数据JSON版