常用数据结构--线性结构
2012-04-27 00:20
281 查看
数据结构是计算机存储、组织数据的方式。常见的数据结构分类方式如下图:
![](http://my.csdn.net/uploads/201204/26/1335449398_7504.png)
常用的线性结构有:线性表,栈,队列,循环队列,数组。线性表中包括顺序表、链表等,其中,栈和队列只是属于逻辑上的概念,实际中不存在,仅仅是一种思想,一种理念;线性表则是在内存中数据的一种组织、存储的方式。
顺序表
顺序表将元素一个接一个的存入一组连续的存储单元中,在内存物理上是连续的。如下图:
![](http://my.csdn.net/uploads/201204/26/1335449295_2066.png)
顺序表存储密度较大,节省空间;但需要事先确定容量,在时间性能方面,读运算较快,时间复杂度为O(1);查找运算为O(n/2),和链表同样;插入运算和删除运算如果要操作中间一个元素,比如3,那么就需要把3后面的元素全部进行移动,因此时间复杂度相对链表要大一些,插入时间复杂度最好为O(0)或最坏为O(n);删除时间复杂度为O([n-1]/2);
链表
链表拥有很多结点,每个结点前半部分是数据域,后半部分是指针域,指针域指针指向下一个结点;链表可分为单链表、循环链表和双链表。
单链表:
![](http://my.csdn.net/uploads/201204/26/1335453699_2018.png)
从上图可以看出,单链表的上一个结点指针指向下一个结点,最后一个结点的指针域为null。
结点的删除:
![](http://my.csdn.net/uploads/201204/26/1335453593_8925.png)
删除一个结点,如删除上图中q结点,只需将p结点中的指针域指向a3,然后将a2释放掉(free)即可。
结点的插入:
![](http://my.csdn.net/uploads/201204/26/1335453914_1570.png)
插入一个结点,如插入上图中s结点,首先将s的指针域指向a2(也就是把s的next赋值为p的next),然后将p结点的指针域指向x即可(p的next指向x)。
循环链表
![](http://my.csdn.net/uploads/201204/26/1335454554_9247.png)
循环链表与单链表唯一不同之处是,循环链表的最后一个结点指针不为空,而是指向头结点。结点的插入和删除和单链表非常相似,就不再示范了。
双链表
![](http://my.csdn.net/uploads/201204/26/1335454807_2626.png)
双链表拥有一前一后两个指针域,从两个不同的方向把链表连接起来,如此一来,从两个不同的方向形成了两条链,因此成为双链表。因此,双链表的灵活度要大于单链表。
结点的删除:
![](http://my.csdn.net/uploads/201204/26/1335455295_4337.png)
双链表的操作比单链表要稍显复杂(按照单链表思路来做其实也不难),如上图,要删除p节点,首先需要将a1的后驱指向a3,然后将a3的前驱指向a1,最后将p节点释放掉即可。
结点的插入:
![](http://my.csdn.net/uploads/201204/26/1335455330_3696.png)
如上图,插入q结点,首先要按照方向,将步骤拆分,首先将q节点的前驱指向p结点后驱,紧接着将x后驱指向a2;然后按照顺序完成图中所示的3、4步即可。(经@llhhyy1989 @voteforvip @wanghuan203 三位童鞋的指正,发现此处有误,正确插入方法可查看评论,为保留错误原文不做改动!不懂具体插入过程可移步:百度知道)
从空间性能来看,链表的存储密度要差一些,但在容量分配上更灵活一些。从时间性能来看,查找运算与顺序存储相同,插入运算和删除运算的时间复杂度为O(1),要更优于顺序存储,但读运算则弱一些,为O([n+1]/2),最好为1,最坏为n。
栈
上面提到栈属于一个逻辑概念,栈的实现可以用顺序也可以用链式。它遵循先进后出原则,如下图:
![](http://my.csdn.net/uploads/201204/27/1335456338_3813.png)
Java中测试代码如下:
输出结果顺序为:初始数量:3,NO3,NO2,NO1,取完后的数量:0。
队列
队列遵循先进先出的原则,如下图:
![](http://my.csdn.net/uploads/201204/27/1335456765_5955.png)
Java中测试代码如下:
运行结果顺序为:初始数量3,NO1,NO2,NO3,取出后数量0。
队列还有一种形式为循环队列,如下图:
![](http://my.csdn.net/uploads/201204/27/1335457040_1711.png)
循环队列有两个指针,头指针head和尾指针tail,尾指针一般指向的不是队尾元素实际地址,而是指向实际地址的下一个空地址,因此,循环队列一般牺牲最后一个空间,用来计算该队列是否满了,判断方式是tail+1 = head,既该队列已满。
为了尽可能的说清楚,插了大量图片,希望理解。以后有时间将继续分析树、图等数据结构。
![](http://my.csdn.net/uploads/201204/26/1335449398_7504.png)
常用的线性结构有:线性表,栈,队列,循环队列,数组。线性表中包括顺序表、链表等,其中,栈和队列只是属于逻辑上的概念,实际中不存在,仅仅是一种思想,一种理念;线性表则是在内存中数据的一种组织、存储的方式。
顺序表
顺序表将元素一个接一个的存入一组连续的存储单元中,在内存物理上是连续的。如下图:
![](http://my.csdn.net/uploads/201204/26/1335449295_2066.png)
顺序表存储密度较大,节省空间;但需要事先确定容量,在时间性能方面,读运算较快,时间复杂度为O(1);查找运算为O(n/2),和链表同样;插入运算和删除运算如果要操作中间一个元素,比如3,那么就需要把3后面的元素全部进行移动,因此时间复杂度相对链表要大一些,插入时间复杂度最好为O(0)或最坏为O(n);删除时间复杂度为O([n-1]/2);
链表
链表拥有很多结点,每个结点前半部分是数据域,后半部分是指针域,指针域指针指向下一个结点;链表可分为单链表、循环链表和双链表。
单链表:
![](http://my.csdn.net/uploads/201204/26/1335453699_2018.png)
从上图可以看出,单链表的上一个结点指针指向下一个结点,最后一个结点的指针域为null。
结点的删除:
![](http://my.csdn.net/uploads/201204/26/1335453593_8925.png)
删除一个结点,如删除上图中q结点,只需将p结点中的指针域指向a3,然后将a2释放掉(free)即可。
结点的插入:
![](http://my.csdn.net/uploads/201204/26/1335453914_1570.png)
插入一个结点,如插入上图中s结点,首先将s的指针域指向a2(也就是把s的next赋值为p的next),然后将p结点的指针域指向x即可(p的next指向x)。
循环链表
![](http://my.csdn.net/uploads/201204/26/1335454554_9247.png)
循环链表与单链表唯一不同之处是,循环链表的最后一个结点指针不为空,而是指向头结点。结点的插入和删除和单链表非常相似,就不再示范了。
双链表
![](http://my.csdn.net/uploads/201204/26/1335454807_2626.png)
双链表拥有一前一后两个指针域,从两个不同的方向把链表连接起来,如此一来,从两个不同的方向形成了两条链,因此成为双链表。因此,双链表的灵活度要大于单链表。
结点的删除:
![](http://my.csdn.net/uploads/201204/26/1335455295_4337.png)
双链表的操作比单链表要稍显复杂(按照单链表思路来做其实也不难),如上图,要删除p节点,首先需要将a1的后驱指向a3,然后将a3的前驱指向a1,最后将p节点释放掉即可。
结点的插入:
![](http://my.csdn.net/uploads/201204/26/1335455330_3696.png)
如上图,插入q结点,首先要按照方向,将步骤拆分,首先将q节点的前驱指向p结点后驱,紧接着将x后驱指向a2;然后按照顺序完成图中所示的3、4步即可。(经@llhhyy1989 @voteforvip @wanghuan203 三位童鞋的指正,发现此处有误,正确插入方法可查看评论,为保留错误原文不做改动!不懂具体插入过程可移步:百度知道)
从空间性能来看,链表的存储密度要差一些,但在容量分配上更灵活一些。从时间性能来看,查找运算与顺序存储相同,插入运算和删除运算的时间复杂度为O(1),要更优于顺序存储,但读运算则弱一些,为O([n+1]/2),最好为1,最坏为n。
栈
上面提到栈属于一个逻辑概念,栈的实现可以用顺序也可以用链式。它遵循先进后出原则,如下图:
![](http://my.csdn.net/uploads/201204/27/1335456338_3813.png)
Java中测试代码如下:
package com.snail.test; import java.util.Stack; public class TestStack { public static void main(String[] args) { Stack<String> stack = new Stack<String>(); stack.push("NO1"); stack.push("NO2"); stack.push("NO3"); System.out.println("初始数量:" + stack.size()); while(!stack.isEmpty()){ System.out.println(stack.pop()); } System.out.println("取完后的数量:" + stack.size()); } }
输出结果顺序为:初始数量:3,NO3,NO2,NO1,取完后的数量:0。
队列
队列遵循先进先出的原则,如下图:
![](http://my.csdn.net/uploads/201204/27/1335456765_5955.png)
Java中测试代码如下:
package com.snail.test; /** * * @author Zang XT */ import java.util.Queue; import java.util.LinkedList; public class TestQueue { public static void main(String[] args) { Queue<String> queue = new LinkedList<String>(); queue.offer("NO1"); queue.offer("NO2"); queue.offer("NO3"); System.out.println("初始数量" + queue.size()); String str; while((str=queue.poll())!=null){ System.out.println(str); } System.out.println("取出后数量" + queue.size()); } }
运行结果顺序为:初始数量3,NO1,NO2,NO3,取出后数量0。
队列还有一种形式为循环队列,如下图:
![](http://my.csdn.net/uploads/201204/27/1335457040_1711.png)
循环队列有两个指针,头指针head和尾指针tail,尾指针一般指向的不是队尾元素实际地址,而是指向实际地址的下一个空地址,因此,循环队列一般牺牲最后一个空间,用来计算该队列是否满了,判断方式是tail+1 = head,既该队列已满。
为了尽可能的说清楚,插了大量图片,希望理解。以后有时间将继续分析树、图等数据结构。
相关文章推荐
- 常用数据结构--线性结构
- 常用数据结构--线性结构
- PAT 数据结构 02-线性结构4. Pop Sequence (25)
- 软件设计师数据结构之线性结构复习小结
- 【数据结构】数据结构再回首之常用排序算法
- 数据结构之线性结构的一些操作总结
- PAT数据结构_02-线性结构4 Pop Sequence (25分)
- 【数据结构】线性表的单链表存储结构表示和实现
- 02-线性结构1 两个有序链表序列的合并——中国大学MOOC-陈越、何钦铭-数据结构-2017秋
- 数据结构 线性表的顺序结构
- 数据结构 学习笔记(三):线性结构:堆栈,队列,表达式求值,多项式加法运算
- PAT 数据结构 02-线性结构3. 求前缀表达式的值(25)
- 数据结构全攻略--线性结构不攻自破(一)
- C语言数据结构-2.线性表之链式存储结构
- 【数据结构郝斌】3、模块一:线性结构
- 数据结构之线性结构--双向链表
- 数据结构(陈越)PAT练习题 第二周:线性结构
- 学习算法和数据结构:线性结构
- Java数据结构-线性结构数组(Array)详解
- 数据结构 | 如何实现线性表的顺序结构