渣渣小本求职复习之路每天一博客系列——数据结构与常用算法(2)
前情回顾:昨天给数据结构与算法开了个头,介绍了下研读思路以及回顾了下数据结构的一些基础概念,例如什么是数据、数据元素、数据项、数据对象,以及数据结构的定义。
明天终于又有个面试,做一些临阵磨枪的准备还是要的,看看半个多月来复习的成果如何。虽然复习的进度不算快,还有很多必须要掌握的方面都没有涉及到,但起码已经复习过的东西要比较熟悉,问到的话能够答出来,而且给出的答案要过得去。不然的话,复习的效果也太不怎么滴了吧。
写完这篇博客之后打算把之前的博客都回顾一遍,算是再做一次复习。大家祝福楼主我吧。
——————————————————————————————闲聊结束————————————————————————————————
在楼主的参考资料《大话数据结构》中,绪论之后所安排的内容是算法的概念。由于进度原因,而且里面的东西基本都是概念性的东西,例如什么是算法,算法效率的度量方法以及大0阶方法都比较熟悉,所以决定跳过,直接进入线性表、栈与队列的介绍。
第二章:线性表
第一节:什么是线性表
线性表(List):零个或多个数据元素的有限序列。如图所示:
线性表的元素的个数n(n>=0)就是线性表的长度,当n=0时,那就成了空表。
第二节:线性表的顺序存储结构
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。
简单的说来,就是在内存中申请了一块连续的内存空间,然后把相同数据类型的数据元素依次存放到里面。既然线性表每个数据元素的类型都一样,所以可以用一维数组来实现这个顺序存储结构。下面,我们来做一个实验,看看是不是连续的内存空间,首先看下面这一段C++
1 #include<iostream> 2 using namespace std; 3 int main(){ 4 int a[10]; 5 for(int i=0;i<10;i++){ 6 int *P=&a[i];//取数组中元素的指针,即内存地址 7 cout<<"第"<<i+1<<"个元素的内存地址是:"<<P<<endl; 8 } 9 return 0; 10 }
运行之后,我们可以看到结果如下图:
地址用的是十六进制,但是我们可以看到他们每个元素的内存地址都相差4,这是因为一个int型占4个字节的内存空间而指针地址指向的是这4个字节的第1个字节的地址。由此可见,线性表(一维数组)在内存中所占用的空间的确是连续的,而且元素的排列也是顺序的。
接下来,让我们聊聊顺序存储结构的插入与删除。
插入算法的思路:
1.如果插入位置不合理,抛出异常;
2.如果线性表长度大于等于数组长度,则抛出异常货动态增加容量;
3.从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置;
4.将要插入元素填入位置i处;
5.表长加1。
删除操作的思路:
1.如果删除位置不合理,抛出异常;
2.取出删除元素;
3.从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置;
4.表长减1。
由此,我们就很容易总结出顺序存储结构的优缺点:
优点:A.无须为表示表中元素之间的逻辑关系而增加额外的存储空间(例如指针)B.能快速存取表中任一位置的元素。
缺点:A.插入和删除都要移动大量元素B.当线性表长度变化较大,难以确定存储空间的容量C.造成存储空间的“碎片”。
根据这些优缺点,说明它比较适合元素个数不大变化,而更多是存取数据的应用。(其实了解一样东西的优缺点,很多时候都是为了根据实际情况选用最合适选项而做的准备。)
第三节:线性表的链式存储结构
顺序存储结构的最大缺点是什么呢?就是插入和删除时需要移动大量元素,非常耗时。为了解决这个问题,链表就出现了。
为了表示每个数据元素与其直接后继元素之间的逻辑关系,除了存储本身的数据之外,还需存储一个指示其直接后继的存储位置。我们通常会把存储数据的域成为数据域,把存储位置的域成为指针域。n个结点链接成一个链表,而刚才说到的链表的每个结点中都只包含一个指针域,所以叫做单链表。如图所示:
通常来说,我们会把链表中的第一个结点的存储位置叫做头指针,整个链表的存取必须是从头指针开始进行。
有时,我们会为了更加方便地对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。在单链表中,我们在C语言中可用结构指针来描述:
/*线性表的单链表存储结构*/ typedef struct Node { ElemType data; struct Node *next; }Node; typedef struct Node *LinkList;/*定义LinkList*/
单链表的读取算法思路:
1.声明一个指针p指向链表的第一个结点,初始化j从1开始
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
3.若到链表末尾p为空,则说明第i个结点不存在;
4.否则查找成功,返回结点p的数据。
相比之下,读取操作会比顺序存储结构效率低得多。但是我们再来看看单链表的插入与删除。
单链表第i个数据插入结点的算法思路:
1.声明一指针p指向链表头结点,初始化j从1开始;
2.当j<1时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
3.若到链表末尾p为空,则说明第i个结点不存在;
4.否则查找成功,再生成一个空结点s;
5.将数据元素e赋值给s->data;
6.单链表的插入标准语句s->next=p->next;p->next=s;
7.返回成功。
删除结点的算法思路:
1.声明一指针p指向链表头指针,初始化j从1开始;
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
3.若到链表末尾p为空,则说明第i个结点不存在;
4.否则查找成功,将与删除的结点p->next赋值给q;
5.单链表的标准删除语句p->next=q->next;
6.将q结点中的数据赋值给e,作为返回;
7.释放q结点;
8.返回成功。
最好大家都用自己熟悉的语言实现一遍,例如C++等。
第三节:单链表结构与顺序存储结构优缺点
我们可以从三个方面对单链表结构和顺序存储结构做对比——
存储分配方式:顺序存储结构用一段连续的存储单元依次存储线性表的数据元素;单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素。
时间性能:查找{顺序存储结构0(1);单链表0(n)。};插入和删除{顺序存储结构0(n);单链表0(1);}
空间性能:顺序存储结构需要预分配存储空间,大了浪费,小了容易发生溢出;单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制,但是存储位置信息要占用空间。
总而言之,选择哪种结构,需要根据实际情况来综合考虑采用哪种数据结构更能满足需求和达到性能要求。
—————————————————————————————THE MIDTERM——————————————————————————————
今晚还是需要复习复习的,所以写的内容少了点。这篇的结尾就不多说什么了,大家祝我好运吧。
重复一下前几天的话:我打算,如果关注我的人多了,就再写一些“文章”(不是现在所写的“随笔”),说一些我想说的话。写写我看过的课外书,讲讲我的经历,跟作为小伙伴们的大家分享我的点点滴滴。(如果想早点看到的话,请点击文章后面levenyes下方的“加关注”哟)