线性表及其逻辑和存储结构(二级)

从线性表开始,我们首先要知道线性表及其结构,线性表有哪些结构,逻辑结构和存储结构,我们主要讲两种存储结构,一个是采用数组

的方式,手动来写一下JAVA里面的ArrayList,链表里面我们讲了一个单链表,我们学过了LinkedList,这个底层采用的不是单链表,而是

双链表,我们会写一个简单的单链表的结构,怎么对单链表做简单的增删改查操作,但是要明确他不是针对LinkedList的

其他链表有双向链表,单链表是单个方向的,还有循环的,收尾相连,JAVA的LinkedList使用的是双向链表,Java中哪些类用到了线性表呢,

Vector: 已经被淘汰了,被ArrayList替换了

ArrayList:

LinkedList: 对于LinkedList我们有一个扩展的内容,比如大家写了单链表之后呢,大家可以研究一下在LinkList里面它是怎么来

实现添加操作的,链表里面怎么加5个数据,增加5个元素,内存里面就会生成一个一个的图,然后代码就这几行就够
我们从头开始讲了,先看第一个

什么叫线性表?

线性表就是n个类型相同元素的有限数列,通常我们记作(a0,a1,.....an-1),a0开始,一直到an-1,索引一般从0开始,从0开始便于地址

的运算,

这个大家要明确,怎么理解这句话呢,首先要记住相同数据类型,这点要明确

1. 相同数据类型: 就是线性表里面可以放很多的数据,但是他们必须是同一种数据类型,比如说都是数字,都是字符,都是学生,都是商品,

   都是装备,但是绝对不可以是线性表里既有数字,又有字符,又有学生,又有商品,不同数据类型是不允许的,因为相同的数据类型在内存
   
   里面分配空间的时候占的空间是一样的,他占的空间是一样的,便于我们后续的查找定位   
   
2. 序列:什么叫做序列,他的序号要记住,他的序号是从0开始的,每个元素都有一个序号,从0开始,哪怕是链表也是有序号的,那对于n个

   数据元素的线性表里面,他的序号一般称之为索引,是从0开始,到n-1,大家要明确,序列,有限的序列,序列是指数是有先后顺序的,
   
   比如ai-1,他的后一个元素就是ai,他是ai的直接前驱,ai是他的直接后继,唯一没有前驱的是第一个,唯一没有后继的是最后一个,
   
   除了第一个和最后一个之外,每个元素有一个唯一的前驱,有一个唯一的直接的后继,满足这个条件的就是线性表,
   
3,有限:有限长度是有限的,不能是无限的,长度为n,n是一个有限值,n为0的时候是一个空表


生活中有哪些案例?

冰糖葫芦满足这个特点,我们从这些具体的图里面,抽象出一个逻辑图,就是线性表的逻辑结构,每一个点我们称之为一个节点,这个节点

可能代表山楂,可能代表人,可能代表分数,可能代表学生,都是有可能的,下面我们再来看存储结构,我们讲两个存储结构,一个叫顺序表,

叫顺序存储结构
有哪些存储结构?

1. 顺序表-顺序存储结构: 特点是在内存中分配连续的空间,只存数据,不存地址,位置就隐含着地址,虽然很熟悉了,还是说明白,

 一下子分配了连续的分配了空间,每个节点占的空间都是一样的,我们来指定他的索引,他的长度是7,每个元素占多长啊,那要看是什么
   
 数据类型了,可以有重复的数据,分配一个连续的空间,有没有存地址,没有才存地址,这一共分配了这么几个空间,没有一个浪费的,
   
 全部用来存数据的,明确一下这是一块连续的空间,只存数据,不存地址,他有什么优点,不存地址的话就节省空间了,没有一个多余的
   
 内容,都是数据,满满的索引查找效率高,什么意思呢,首地址1024,第二个是1028,因为整数占4个,第7个1068,就是这么来的啊,
   
 他这里面最大的优点就是,我想按照索引来获取,我想获取第0个,第5个元素花的时间是一样的,为什么呢,数组的
   
 起始地址+每个元素的大小*索引,花的时间都是一样的,我想知道第0个,当然是按照索引来查的,1024加上每个元素的长度,
   
 1024+4*0=1024,你看他找到了吧,我想找第5个,1024+4*5=1044,索引是5的这个,是不是1044,这个时间是特别快的,
   
 我要获取第n个呢,1024+4*n,大家要记住数组最大的优点就是这个,找数据特别的快,这是他的优点,我们这里都有
   
 一个相应的说明,有没有缺点,有,有什么缺点,我要删除,我们要找索引是2的元素,更新不是删除,他还占着一个位置,
   
 什么叫删除,我想添加一个元素,就得从后向前移动,所以这是他的一个缺点
   
 什么缺点呢?
   
1. 插入和删除需要大量的移动元素,效率是比较低的
   
2. 因为比如JAVA里面的数组,需要实现分配一块连续的空间,那这个空间有多大呢,假如我一开始分配了100个,结果你就占了2个,
   
 那还有98个是闲置的,就会导致浪费,那有人说那就分配5个呗,万一不够了呢,是不是可能不够,所以这是他的一个缺点,分配多了
	  
 会导致浪费,分配少了就不够了,当然不够我们也有办法,就是ArrayList不够了动态的扩容就可以了,但是你要扩容还是要分配
	  
 更多的空间,还要做一些转移的操作,这是一个缺点,还有什么缺点呢,我想找索引是3的,索引是5的,直接套这个式子就可以了,
	  
 当是如果我想看一下我这里面有没有89这个数,怎么办啊,不是找索引了,是找值了,只能逐个的比较,这效率是比较低的,大家
	  
 想一下,如果长度是n,那你的效率好的话,你找的是56,一次就找到了,如果你找的是80,是不是要找到最后,如果我们找的
	  
 每个节点的概率是一样的话,如果总的长度是n,你需要找二分之n次,需要找一半,为什么要找一半,我们算一下啊,
	  
 如果出一个题目让你算算,数组中按照内容查询元素的时间复杂度是多少,T(n)=O(n),为什么是O(n)这只是一个规模,
	  
 这不是一个具体的数,我们来算一下,一共有n个元素,找每个元素的概率是一样的,他的概率是多少啊,概率是n分之一,
	  
 找每个元素的概率都是n分之一的,概率相同,每个元素的概率是1/n,请问找第一个元素要花多长时间,找第一个元素
	  
 第一次就可以了,找索引是3的1+2+3+4次,找第n个的时候,那就需要n次了,这是不是总的次数,把它们加起来(1+2+3+4+....+n),
	  
 乘以每个元素的概率,乘以1/n,有人说这不好理解那我们这么做,找第一个元素1*(1/n),找第二个元素2*(1/n),找两次,
	  
 概率也是n分之一,在加上3*1/n,最后加上n*1/n,和上面的式子一样的,(1+n)n/2*n,最后等于n/2+1/2,这是平均的查找次数,
	  
 我们算的不是这一个,我们要的是级别,把1/2的常数项去了,把这个系数去了,我们大家以后要会算时间复杂度,
	  
 概率相同的情况下要会算,我要把一个元素删除,他的时间复杂度是多少,那你要看删除谁呢,你要删除56,我要把56删除了,
	  
 那麻烦大了,那都得动,如果我要是把80删除,是不是不用动了,同样按照我们
	  
 刚才的公式来算
	  
 举例: 长度为n的数组中删除元素,假设每个元素删除的概率是相同的,问世间复杂是多少?
	  
 按照我们相同的办法,最后得到T(n)=(n-1)/2,时间复杂度还是n,T(n)=O(n),这个大家要明白
	  
 这个就是我们讲的顺序表的优点和缺点,
   

2. 链表-链式存储结构: 我们来看链表,链式存储结构,他有什么特点?

  数据元素的存储对应不连续的空间,地址是不连续的,我们搜一个单链表,找一个图片,在内存里面是不连续的空间,怎么不连续了,
	  
 head我们先不管他,这个可能是1024,这个可能是5012,这个可能是2558,这个可能是9999,有没有规律,完全没有规律,是不连续的
	  
 空间,那问题就来了,我这里面存的是A第一个元素,我读了A之后我怎么找到B,这地址是没有任何规律,那应该怎么办,每个节点
	  
除了要存数据之外,这里还要存一个5012,引用就是指针,地址读了这个A之后,根据这个地址跳到这儿来,找到B了,这应该放2558
	  
 找到C了,C就放9999,这已经是最后一个了,最后一个方null,表明到了末尾了,我想再追加一个元素呢,那你就这么来追加呗,
	  
 他里面是有值的,值比如我们给个Z,那这个地址是多少,地址是8888,它是最后一个元素,在这里要补上一个null,我们是在
	  
 最后追加的,关键我怎么知道E的后面是这个,把E的null改成8888,注意啊,这8888不是一个数字,是一个地址,就像对象的引用一样,
	  
 他就指向Z了,这是我们讲的一个单链的,他有什么缺点,首先你看,数组里面不要存地址,链表里面有额外的地址存储地址,这个地址
	  
 是为了找到下一个,链表从这个角度来看是要多占一些空间,多占一些空间来指向下一个元素,还有一个,这个是索引为0的,这个是索引
	  
 为1的,这个是索引为2的,这是索引为3的,这是索引是4的,我想找索引是4的元素,怎么办,你怎么找到,我们用1024+4*几有用吗,
	  
 地址完全没有规律,算不出来,不能算,所以第二个缺点,按照索引来查找,只能逐个的从头开始,我想找索引是4的,就数呗,需要逐个
	  
 的比较,逐个的后移,不是一次计算到位的,他的效率就是比较低的,你发现他和数组正好相反,但它有个优点,我把这个2删除怎么办,
	  
我想把B这个元素删除了,删除了他,没有了,后面的不要动,只要做一个操作就行了,前面的就不要写5012,这个要写2558,只要你写个2558
	  
 他就指向了2558了,A的下一个元素就是C了,而B垃圾回收了,我们做删除的话,后面需不需要移动啊,不要移动,只是本来就没有规律,
	  
 只要给下个指针就可以了,我想再E和C中间加上一个节点,那应该怎么办,我在C和E之间加一个元素,后面的元素还要动吗?
	  
 不用,直接增加一个节点,我们要在C和E之间加一个节点,元素叫K,他没有加到链表里面,我们给K取个地址叫7777,K的地址要指向9999,
	  
 为什么呢,你这里存的9999相当于指向E,不对啊,你没有说C的下一个是K啊,C指向的地址就不是9999了,这个指向关系就不存在了,
	  
 这里应该写7777,这不就连起来了吗,在我们的链表里面做删除和添加操作的话,还是比较方便的,需要修改相应的指针就可以了,
	  
 你想在C和E之间加上一个节点,你是不是要先找到C和E,是不是还得从头找,这是我们讲的一个内容,优点和缺点都已经写好了
	  
 优点:
	  
 1. 插入和删除灵活,效率比较高,
	  
 2. 还有一个,链表到底是浪费空间还是节省空间呢,他需要存地址,它是浪费空间的,多占用一些空间的,反过来你会发现,
	  
	他不存在闲置的情况他不会有这种情况,这里面引着一个节点,里面没有值,然后他又引了一个节点,里边没有值,
		 
	他会不会有这种情况,既然存在着节点,都是有值的所以不会像数组一样,数组里面一下子分配了10个空间,结果就用了3个,
		 
	暂时空闲着7个,不用的时候就是一个空的链表,一个元素也没有,从这个角度来说,这个链表又是节省空间的,所以以后
		 
	在面试的时候,链表是浪费空间还是节省空间,那要从不同的角度来说,这个就收我们讲的链表的优点和缺点,
		 
	包括他的一个特点,我给大家说到这儿,
   
   
在这里比较细致的说一下,理解成我们实际开发中的数组就可以了

猜你喜欢

转载自blog.csdn.net/Leon_Jinhai_Sun/article/details/89556184