1. 数据结构基础
1.1 认识数据结构
-
数据结构就是指数据之间的关系
-
算法+数据结构=算法
-
数据结构就是研究数据的逻辑结构、存储结构和运算方法(即算法)的学科
数据结构是计算机基础课程,也是学习计算机十分重要的课程。在以后的编程道路,或者考研道路上都是避不开的一门课程。所以有想考研或者以后想从编程道路的战友们,一定要在这门课上下功夫。
1.2 数据结构研究的内容
用计算机解决一个具体问题时,要经过一下三个步骤:
-
从具体问题抽象出适当的数学模型
-
设计求解数学模型的算法
-
编制、运行并调用程序,直到解决实际问题
1.3 数据的逻辑结构
-
数据:数据就是信息的载体,对客观事物的符号表示。(通俗来讲就是你想处理的问题,必须转化成计算机符号,这样计算机才能识别)。这里的信息是指被赋予特殊意义的数据。
信息是数据的子集
-
数据元素:对现实世界中某独立个体的数据描述,数据的基本单位,有时称为结点。
-
数据项:数据不可分割的、具有独立意义的最小数据单位,是对数据元素属性的描述。
三者之间的关系:数据>数据元素>数据项
-
数据对象:性质相同的数据元素的集合,是数据的一个子集
-
数据结构:相互之间存在一种或多种特定关系的数据元素的集合
-
数据结构的分类:
-
集合结构:数据元素“同属一个集合”,数据之间没有其他联系
-
线性结构:单个数据元素有且仅有一个开始结点和终端结点
-
树形结构:“一对多关系”
-
图形结构:“多对多关系”
-
1.4 逻辑结构的描述
数据结构:是指数据对象及其相互关系和构造方法。一个数据结构G可以用一个二元组表示为:G=(D,R)其中:D是数据元素的非空集,R是在D上所有数据元素之间的关系的非空有限集合。
1.5数据的存储结构
-
顺序存储:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。(地址连续的存储)
-
链式存储:每个数据元素有两种元素组成(数据区、指针区),每个数据元素以结点的方式存储。
后一个数据元素的地址在前一个结点的指针区,最后一个数据元素的指针区为空,第一个数据元素必须设置一个头指针存放它的地址。
1.6 算法和算法分析
-
算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每条指令表示一个或多个操作。
-
算法的特性:
-
有穷性:求解问题步骤有限
-
确定性:任何情况下对同一问题的求解结果必须唯一
-
可行性:法律,经济,技术上都可行
-
输入:没有输入
-
输出:没有输出
-
-
算法和程序的关系:
-
一个算法必须在有穷步骤之后结束,但是程序不一定满足有穷性
-
程序中的指令必须是机器可以执行的,算法中的指令没有这种限制
-
算法代表了对问题的求解过程,而程序则是算法在计算机上的实现
-
算法与数据结构相辅相成(算法+数据结构=程序)
-
-
算法效率评价
-
时间复杂度:算法运行所消耗的时间
-
空间复杂度:程序从开始到结束所需要的存储空间
-
## 2.线性表(重点)
### 2.1 线性表的定义与运算
1. 线性表就是线性的数据结构,其特点是前驱后继一对一。线性表是由同一类型的数据元素构成的线性结构。
2. 线性表是n(n>=0)个相同属性的数据元素的有限序列。其中n**叫做线性表的长度**,若n=0,称作空表。a0称为起点,a(n-1)称为终点。
3. **线性表的特点**:与线性结构特点相同。
* 存在唯一一个“第一个”元素和“最后一个”元素
* 除了第一个和最后一个以外,每个元素都只有一个前驱一个后驱
### 2.2 线性表的基本操作
1. 创建线性表
2. 求线性表的长度
3. 查找操作
4. 删除操作
5. 显示操作
6. 插入操作
* 线性表的顺序存储结构:用一组地址连续的存储单元依次存储线性表中的各个元素。
*Loc(ai)=Loc(a0)+i*L;(L为每个数据元素所占的内存)
顺序表的插入和删除
线性表结构简单,易于实现,但插入或删除一个元素时需对其后继的全部元素进行逐个移动。这样需花费较多时间,特别是插入元素而需扩大储存时,线性表后面的储存区可能已经被占用从而无法扩大。所以,这种表结构仅适用于数据元素不经常变动或只需在顺序存储设备上做成批处理的场合。 下面讨论线性表的插入和删除操作。
从线性表中删除一个数据:需要将最后一个元素清零,否则最后一个元素还和之前最后一个元素一样,线性表所占内存还不变。
从线性表插入一个元素(算法):
用函数实现算法如下:
int ins1(int V[],int i,int b) { int j; if(i<0 || i>98){ //判断要插入的位置是否在列表长度中 printf("插入失败!"); return 0; } for(j=99;j>i;j++) //将要插入的位置后方的数往后挪一位 V[j+1]=V[j]; V[j]=b; //插入数据 return 1; }
在有序表中插入一个元素
-
设一个有序线性表,用数组结构实现。最大元素个数为n。设当前元素个数是m.要求在该有序表中插入一个值为x=63,的元素,示意图与流程图如下:
代码实现:
int ins2(int a[],int n,int m,int m) { int i; if(m>=n){//判断表的储存是不是够! printf("插入失败!"); return 0; } for(i=m-1;i>=0&&a[i]>x;i--) a[i+1]=a[i]; a[i+1]=x;//插入 m++; reutrn 1;//插入成功! }
删除线性表的一个元素
-
删除一个元素V[i]:
-
int del1(int V[],int i) { int j; if(i<0||i>99){ printf("删除失败!"); return 0; } for(j=i;j<99;j++) V[j] = V[j+1]; V[99]=0; return 1; }
线性表的链式存储
1.线性链表
-
可以随机存取表中的任何一个元素,但是这一特点也铸成存储结构的三个弱点
-
在插入或删除时,需要移动大量元素
-
表的容量难以根据实际扩充
-
在长度变化较大的线性表预先分配空间时,必须按最大内存分配,内存使用浪费
-
-
表中的每个数据元素都是独立的,每个元素叫做结点
-
头结点:再开始结点之前附加的一个结点,数据区存储的内容是该链表附加信息。
-
开始结点是存储第一个数据元素的结点
-
头指针:指向链表中第一个结点的指针。
通常用头指针来标识一个线性表,如线性表L,是指某链表的第一个结点的地址放在了指针变量L中。头指针为NuLL则标识一个空链表。
-
-
申请一个结点可以用C++语言描述:
p = new LinkNote;
用完释放:delete(p)
-
指针区:next,Link
-
数据区:date
链表插入和删除算法
-
链表末尾接上结点
void lq_ins3(hpt,x) LinkNode *hrt;//链表头指针 int x; { LinkNode *u,*p; u=new LinkNode; u->data=x; u->link=null; if(hpt == null){ hpt = u; return 1; }//x从链表首结点开始走向末尾结点 p=hpt; While(p->link!=null) p=p->link;//p后一一个位置 p->link=u; }
-
有序链表插入新节点
void lq_ins4(hpt,x) LinkNode *hpt; int x;//新节点的键值 { LinkNode *u,*p,*q; u=new LinkNode; u->data = x;//从链表首部开始寻找 p=hpt; While(p&&p->data<x){ q=p; p=p->link; } if(p=hpt){hpt=u;u->link=null}//空链表时候 if(p->data<x){p->link = u;u->link=null;} else{ q->link=u; u->link=p; } }
-
链表中删除指定的结点
int lq_delete(hpt,a) LinkNode *hpt; int a; { LinkNode *p,*q; if((p=hpt)==NULL) return 1;//链表已为空链表 if(p->data == a){ //要删除链表的首结点 hpt = p->link; // 更改链表头指针 delete(q);//释放被删除结点的空间 return 0; } while(q->data!=a&&p->link!=NULL){ p=q;q=q->link; //寻找指定的数字 } if(q->data == a){ p->link=q->link;//被删除的结点从链表脱钩 delete(q); //释放被删除结点的空间 return 0; } // return 1;//没有找到匹配的结点 }
循环链表
1.单循环链表
-
将单向链表中最后一个结点的指针区指向头结点或开始接结点,形成了一个环,就构成了单向循环链表。(p->link = head 充要条件)
2.存储密度
-
数据所占用的存储空间/结点所占用的存储空间 = 存储密度
-
顺序表的存储密度等于一,链表的存储密度小于一
双向链表:每一个结点有一个数据区和两个指针区(前指针区llink 后指针区 rlink)
-
双向循环链表p->rlink=head
(p->front)->rear = p;
(p->rear) ->front = p;
head->llink=p
3.双向链表的插入操作
主要操作步骤如下:
-
q->rear = p->rear;
p->front = p;
p->rear = q;
(p->rear)->front = q;
4.删除
主要步骤如下:
-
(p->front)->rear = p->rear;
(p->rear)->front = p->front;
delete(p);