数据结构期末复习+代码

数据结构

本人的期末数据结构期末考试复习整理的知识点,把知识点理解一遍,有条件的话再结合书后习题练习一下(特别是二叉树和图论),成绩90+很轻松

数据结构的主要研究内容是非数值问题

数据:客观事务的符号表示,是所有能够输入计算机并被计算机程序处理的符号的总称

数据元素:数据的基本单位,在计算机中通常以一个整体进行考虑和处理

数据项:组成数据元素、有独立含义、不可分割的最小单位

数据对象:性质相同数据元素的集合,是数据的一个子集

数据结构:相互存在一种或多种特定关系的数据元素的集合

逻辑结构:从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的

逻辑结构的两个要素:数据元素和关系

逻辑结构的四类基本结构集合结构线性结构树结构图结构或网状结构

抽象数据类型:由用户定义的、表示应用问题的数学模型以及定义在这个模型上的一组操作的总称

抽象数据类型的三大部分数据对象数据对象上的关系集合数据对象的基本操作集合

顺序存储结构,借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系

链式存储结构,为表示节点之间的关系,给每个节点附加指针字段,用于存放后继元素的存储地址

算法的五个重要特性,有穷性、确定性、可行性、输入、输出

算法的四个评判标准,正确性、可读性、健壮性、高效性

衡量算法效率的方法:事后估计法、事前分析估计法

不考虑计算机的软硬件等环境因素,影响算法时间代价的最主要因素是问题规模

时间复杂度,logn<n<nlogn<n^2<2^n;

算法的时间复杂度与问题的规模有关,还与待处理数据的初态有关

顺序表是随机存储的结构,链表是顺序存储的结构

顺序表的初始化

Status InitList(SqList &L){

   L.elem = new ElemType[MaxSize];

   if(!L) exit(OVERFLOW);

   L.length = 0;

}

取值

Status GetElem(SqList L,int i, ElemType &e){

   if(i<1||i>L.length) return ERROR;

    e = L.elem[i-1];

    return OK;

}

查找

int LocateElem(SqList L,ElemType e){

    for(i=0;i<L.length;i++){

       if(L.elem[i] == e) return i+1;

}

   return 0;

}

插入

Status ListInsert(SqList &L,int i,ElemType e){

    if(i<1 || i>L.length+1) return ERROR;

   if(L.length == MaxSize) return ERROR;

    for( j = L.length-1; j>=i-1;j--){

       L.elem[j+1] = L.elem[j];

}

    L.elem[i-1] = e;

    L.length++;

   return OK;

}

平均时间复杂度:2/n

删除

Status ListDelete(SqList &L,int i){

    if(i<1 || i>L.length)  return ERROR;

    for(j=i;j<L.length;j++){

       L.elem[j-1] = L.elem[j];

}

   L.length--;

   return OK;

}

平均时间复杂度:n-1/2

长度为N的顺序表中,插入一个新元素平均需要移动表中N/2个元素,删除一个元素平均需要移动N-1/2个元素

链表的初始化

Status InitList(LinkList &L){

    L = new LNode;

    L->next = null;

    return OK;

}

取值

Status GetElem(LinkList L,int i,ElemType &e){

   p = L->next;j=1;   

   while(p&&j<i){  

       p = p->next;

        j++;

}

   if(!p || j>i) return ERROR;

   e = p ->data;

   return OK;

}

查找

LNode  *LocateElem(LinkList L,ElemType e){

p = L->next;j = 0;   

while(p&&p->data!=e){

p= p->next;

}

return p;

}

插入

Status ListInsert(LinkList &L,int i,ElemType e){

    p  = L; j = 0;

    while(p&&j<i-1){

       p = p->next;

       j++;

}

   if(!p || j>i-1) return ERROR;

    s = new LNode;

    s->data = e;

    s- >next = p - >next;

    p->next = s;

    return OK;

}

时间复杂度:O(n)

删除 

Status ListDelete(LinkList &L,int i){

     p =L; j = 0;

    while(p->next&&j<i-1){

       p = p->next;

       j++;

   }

    if(!p || j>i-1) return ERROR;

    q = p->next;

    p->next = q->next;

   delete q;

    return OK;

}

时间复杂度 :O(n)

插入与删除的循环条件不同,是因为插入的合法位置有n+1个,删除的合法位置有n个,如果删除的循环条件与插入一致,会出现空指针的情况

前插法创建单列表 头结点与首元结点之间

void CreateList_H(LinkList &L,int n){

L = new LNode;

L->next = null;       

for(i=0;i<n;i++){

p = new LNode;

cin>>p->data;

p ->next = L->next;

L->next = p;   

   }

}

后插法创建单链表 所有节点之后

void CreateList_R(LinkList &L,int n){

L = new LNode;

L ->next = null;

r = L;

for(i = 0;i<n;i++){

cin>>p->data;

p->next = null;

r ->next = p;

r = p;

}

}

顺序表的存储密度为1,链表的存储密度小于1。

进行存取操作考虑顺序表,进行插删操作考虑链表。

顺序表的合并

void MergeList_Sq(SqList LA,SqList LB,SqList &LC){

LC.length = LA.length+LB.length;

LC = new ElemType[LC.length];

pc =LC.elem, pa = LA.elem, pb = LB.elem;

pa_last = LA.elem+LA.length-1;

pb_last = LB.elem+LB.length-1;

while(pa<=pa_last && pb<=pb_last){

if(*pa<=*pb){ *pc++=*pa++;     }

else *pc++=*pb;

}

while(pa<=pa_last){*pc++=*pa;}

whilel(pb<=pb_last){*pc++=*pb;}

}

链表的合并

void MergeList_L(LinkList &LA, LinkList &LB, LinkList &LC){

LC = LA;//直接合并到LA后面

pc = LC, pa = LA->next, pb = LB->next;

while(pa&&pb){

if(pa->data<=pb->data){ pc->next = pa, pc = pa, pa=pa->next}

else pc->next = pb, pc = pb, pb=pb->next;

pc ->next = pa?pa:pb;

delete LB;要把LB的头结点删掉

}

}

比较项目

顺序表

链表

存储空间

预先分配,容易造成空间闲置或溢出

动态分配,不会溢出或闲置

存储密度

等于1

为表示节点之间的逻辑关系而增加额外的存储开销,小于1

存储元素

随机存储,访问时间复杂度O(1)

顺序存储,访问时间复杂度O(n)

插入、删除

时间复杂度O(n)

时间复杂度O(1)

使用情况

长度变化不大,不经常使用插删操作

长度变化较大,经常进行插删操作

顺序栈的初始化

Status InitStack(SqStack &S){

S.base = new SElemType[MaxSize];

if(!S.base) exit(OVERFLOW);

S.top = S.base;

S.stackSize = MaxSize;

return OK;

}

入栈

Status Push(SqStack &S, SElemType e){

if(S.top-S.base==MaxSize) return ERROR;

*S.top = e;

S.top++;

return OK;

}

出栈

Status Pop(SqStack &S,SElemType &e){

if(S.top==S.base) return ERROR;

e = *--S.top;

return OK;

}

取栈顶

SElemType GetTop(SqStack S, SElemType &e){

if(S.base!=S.top){ return *(S.top-1);}

}

链栈的初始化

Status InitStack(LinkStack &S){

S = null;

return OK;

}

入栈 头插,连接到栈顶上方,且链栈没有设置栈顶与栈底指针

Status Push(LinkStack &S, SElemType e){

p = new StackNode;

p ->data = e;

p->next = S;

S = p;

return OK;

}

出栈

Status Pop(LinkStack &S , SElemType &e){

if(S==null) return ERROR;

e = S->data;

p = S;

S = S->next;

delete p;

return OK;

}

取栈顶

SElemType GetTop(LinkStack S, SelemType &e){

if(S!=null) return S->data;

}

汉诺塔算法(要能够掌握递归的过程,有考题会让画递归调用图)

void move(char A,int n,char C){

count<<n<<","<<A<<C;

}

void Hanoi(int n, char A,char B, char C){

if(n==1) move(A,1,C);

else{

Hanoi(n-1,A,C,B);

move(n,A,B,C);

Hanoi(n-1,B,A,C);

}

}

顺序队列的初始化 队首与队尾都是整数

Status InitQueue(SqQueue &Q){

Q.base = new QElemType[MaxSize];

if(!Q.base) exit(OVERFLOW);

Q.front = Q.rear = 0;

return OK;

}

入队 对于循环队列的队首与队尾指针改变都需要对队长取余

Status EnQueue(SqQueue &Q, QElemType e){

if((Q.rear+1%MaxSize)==Q.froznt) return ERROR;

Q.base[Q.rear] = e;

Q.rear=(Q.rear+1)%MaxSize;

return OK;

}

出队

Status DeQueue(SqQueue &Q,QElemType &e){

if(Q.rear == Q.front) return ERROR;

e = Q.base[Q.front];

Q.front = (Q.front+1)%MaxSize;

return OK;

}

取队首

QElemType GetHead(SqQueue Q,QElemType &e){

if(Q.front!=Q.rear){

return  Q.base[Q.front];

}

}

链式队列的初始化 front不存储,入栈直接连接到队尾后

Status InitQueue{

Q.front = Q.rear = new QNode;

Q.front ->next  =null;

return OK;

}

入队 尾插

Status EnQueue(LinkQueue &Q, QElemType e){

p = new QNode;

p->data = e;

p->next = null;

Q.rear->next = p;

Q.rear = p;

return OK;

}

出队

Status DeQueue(LinkQueue &Q, QElemType &e){

if(Q.rear==Q.front) return ERROR;

p = Q.front->next;

e = p->data;

Q.front->next = p->next;

if(Q.rear = p) Q.rear = Q.front;//删除了最后一个元素

delete p;

return OK;

}

取队首

QElemType GetHead(LinkQueue Q, QElemType &e){

if(Q.rear!=Q.front) {

return Q.front->next->data;

}

}

进制转换

void converion(int N){

InitStack(S);

while(N){

Push(S,N%8);

N/=8;

}

while(!StackEmpty(S)){

Pop(S,e);

count<<e;

}

}

括号匹配

Status Matching(){

InitStack(S);

flag = 1;

cin>>ch;

while(ch!='#'&&flag){

Swith(ch){

case  '[':

case '{':

Push(S,ch);

break;

case ']':

if(!StackEmpty(S)&&GetTop(S)=='['){

Pop(S,e);

}

else flag = 0;

break;

case '}':

if(!StackEmpty(S)&&GetTop(S)=='}'){

Pop(S,e);

}

else flag = 0;

break;

}

}

if(flag&&StackEmpty(S)) return true;

else return false;

}

队列应用于打印机、模拟排队场景

堆可以为每一个新产生的串动态分配一块实际串长需要的存储空间

模式匹配算法 BF算法(牢记i指针和i指针的回溯位置)

int Index_BF(SString S, SString T, int pos){

i = pos, j =1;

while(i<=S.length && j<=T.length){

if(S.ch[i]==T.ch[j]) i++, j++;

else{

i = i-j+2;

j = 1;//匹配失败,重新追溯

}

}

if(j>T.length) return i-T.length;//子串匹配完成,成功

else return 0;

}

最好情况时间复杂度,O(n+m)

最坏时间复杂度,O(n*m)

时间复杂度,O(m*n)

求子串的个数=n*(n+1)/2+1

低下标优先优先存储类似行优先存储,高下标优先存储类似于列优先存储

对称矩阵,以行优先为例(具体考虑是从1开始存储还是0开始存储,0开始存储需要减1,1开始存储不需要减1)

i*(i-1)/2+j-1

i>=j

下三角

j*(j-1)/2+j-1

i<j

上三角

三角矩阵,从1开始

(i-1)*(2n-i+1)/2+(j-i)

i<=j

上三角矩阵

0或n*(n+1)/2

i>j

上三角矩阵

i*(i-1)/2+j-1

i>=j

下三角矩阵

0或n*(n+1)/2

i<j

下三角矩阵

广义表的长度是指表中子表个数,深度指子表中元素的最大个数

广义表的表头可能是一个单原子,也可能是一个子表;表尾一定是一个广义表

计算规则(牢记)根据节点数计算二叉树有多少种=(1/n+1)*C2n  n

二叉树的第n层最多为2^(n-1)个,深度为k的二叉树至多有(2^k)-1个节点证明过程

总节点数N=n0+n1+n2,其中度0节点比度2节点多一个,即n0=n2+1,所以N=2*n2+n1+1,证明过程

具有n个节点的完全二叉树的深度为(log 2 n)+1,证明过程

顺序存储结构仅适用于完全二叉树,对于一般的二叉树采用的是链式存储方式

含有n个节点的二叉链表中有n+1个空链域

中序遍历

void InOrderTraverse(BiTree T){

if(T){

InOrderTraverse(T->lchild);

count<<T->data;

InOrderTraverse(T->rchild);

}

}

中序遍历,非递归算法

void InOrderTraverse(BiTree T){

InitStack(S),p = T;

q = new BitNode;//用于暂时存放栈顶弹出的元素

while(p || !StackEmpty(S)){

if(p){

Push(S,p);

p = p->lchild;

}

else{

Pop(S,q);

count<<q->data;

p = q->rchild;//访问右子树

}

}

}

递归与非递归遍历二叉树,无论采用哪一种次序进行遍历,含n个节点的二叉树,其时间复杂度为O(n),空间复杂度也为O(n)

二叉树的先序与中序,或中序与后序能唯一地确定一颗二叉树

先序创建二叉链表,递归

void CreateBiTree(BiTree T){

cin>>ch;

if(ch=='#'){

T = NULL;

}

else{

p = new BiTNode;

p->data = ch;

CreateBiTree(T->lchild);

CreateBiTree(T->rchild);

}

}

复制二叉树,递归

void Copy(BiTree T, BiTree &NewT){

if(!T){

NewT = NULL;

return;

}

esle{

NewT = new BiTNode;

NewT->data = T->data;

Copy(T->lchild,NewT->lchild);

Copy(T->rchild,NewT->rchild);

}

}

计算深度,递归

int Depth(BiTree T){

if(T==NULL) return 0;

else{

m = Depth(T->lchild);

n = Depth(T->rchild);

if(m>n) return (m+1);

else return (n+1);

}

}

统计各节点个数

int NodeCount(BiTree T){

if(T==NULL) return 0;

else{

return NodeCount(T->lchild)+NodeCount(T->rchild)+1;

}

}

int SingleNode(BiTree T){

if(T==NULL) return 0;

if(T->lchild==NULL&&T->rchild!=NULL || T->lchild!=NULL&&T->rchild==NULL) return 1;

else{

m = SingleNode(T->lchild);

n  = SingleNode(T->rchild);

num =  m+n;

}

return num;

}

int LeafNode(BiTree T){

if(T==NULL) return 0;

if(T->lchild==NULL && T->rchild==NULL) return 1;

else{

m = LeafNode(T->lchild);

n  = LeafNode(T->rchild);

num = m + n;

}

return num;

}

int DoubleNode(BiTree T){

if(T==NULL || (!T->rchild && !T->lchild)) return 0;

else{

if(T->rchild && T->rchild){

num++;//只要不是空或者、单分支、度0节点

num+ =DoubleNode(T->lchild);

num+ =DoubleNode(T->rchild);

}

}

return num;

}

树的先根和后根序列遍历依次对应先序与中序遍历;森林的先序和中序遍历依次对应先序与中序遍历

构造哈夫曼树,n个叶节点的哈夫曼树共有2n-1个节点

初始化:首先申请2n个单元,将所有单元的双亲,左孩子,右孩子的下标初始化为0,最后循环n次输入前n个单元的叶子的权重;

创建:通过n-1次的选择、删除、合并

选择是选择双亲为且权重最小的两个树根节点s1和s2;

删除是将s1,s2的双亲改为非0;

合并是将s1,s2的权值之和作为一个新的节点存入数组n+1号及之后的单元中,同时记录这个节点的左孩子与右孩子是s1,s2

void CreateHuffmanTree(HuffmanTree &HT, int n){

if(n<=1) return;

m = 2*n-1;

HT = new HTNode[m+1];//0号单元未使用

for(i = 1;i<=m;i++){HT[i].parent = 0, HT[i].lchild = 0, HT[i].rchild = 0;}

for(i = 1;i<=n;i++){cin>>HT[i].weight;}

for(i = n+1;i<=m;i++){

Select(HT,i-1,s1,s2);

HT[s1].parent = i, HT[s2].parent = i;

HT[i].lchild = s1, HT[i].rchild = s2;

HT[i].weight = HT[s1].weight+HT[s2].weight;

}

}

哈夫曼编码是前缀编码,是最优前缀编码

所有点的度数和 = 2*边数和

n个顶点的无向图最多有 n*(n-1)/2条边

n个顶点的有向图·最多有 n*(n-1)条边

连通图的连通分支量为 0

有向树,有一个顶点入度为,其余顶点的入度均为1的有向图

邻接矩阵的存储结构

typedef struct{

VerTexType vexs[MVNum];//顶点表

ArcType arcs[MVNum][MVNum];//邻接矩阵

int vexnum,arcnum;//顶点数,边数

}AMGraph;

邻接矩阵表示法创建无向网

Status CreateUDN(AMGraph G){

cin>>G.vexnum>>G.arcnum;

for(i=0;i<G.vexnum;i++){

cin>>G.vexs[i];

}

for(i=0;i<G.vexnum;i++){

for(j=0;j<G.vexnum;j++){

G.arcs[i][j] = MaxInt;

}

}

for(k=0;k<G.arcnum;k++){

cin>>v1>>v2>>w;

i  = LocateVex(G,v1), j = LocateVex(G,v2);

G.arcs[i][j] = w;// 如果是图,只需要赋值为1

G.arcs[j][i] = G.arcs[i][j];//如果是有向,则不需要对称复制

}

return OK;

}

对于无向图,第i行就是Vi的度;对于有向图,第i行是Vi的出度,第i列是Vi的入度

一个图的邻接图唯一,但邻接表不唯一,邻接表中各节点的链接次序取决于邻接表的算法还有边的输入顺序

邻接矩阵表示的优缺点(对比着记忆0)

优点

便于判断两个顶点之间是否有边;便于计算各顶点的度

缺点

不便于增加与删除顶点;不便于统计边的条数;空间复杂度更高

邻接表表示的优缺点

优点

便于增加和删除顶点;便于统计边的条数;空间利用率更高

缺点

不便于判断顶点之间是否有边;不便于计算顶点的度数

深度优先搜索——树的先序遍历的推广,是一个递归的过程,借助栈

采用邻接矩阵,深度优先搜索遍历

void DFS(Graph G, int v){

visited[v] = true;

cout<<G.vexs[v];

for(w = 0;w<G.vexnum;w++){

if(G.arcs[v][w]!=0 && G.arcs[v][w]!=MaxInt && !visited[w]){

DFS(G,w);

}

}

}

采用邻接表,深度优先搜索遍历

void DFS(ALGraph G, int v){

visited[v] = true;

cout<<G.vertices[v].data;

for(p = G.vertices[v].firstarc;p;p=p->nextarc){

w = p->adjvex;

if(!visited[w]) DFS(G,w);

}

}   

广度优先遍历——树的层次遍历,借助队列

以邻接表为例

void BFS(Graph G, int v){

cout<<v,visited[v] = true;

InitQueue(Q);

EnQueue(Q,v);

while(!QueueEmpty(Q)){

DeQueue(Q,u);

for(p = G.vertices[u].firstarc;p;p=p->nextarc){

w = p->adjvex;

if(!visited[w]){

cout<<w;

visited[w] = true;

EnQueue(Q,w);

}

}

}

}

深度优先搜索和广度优先搜索的时间复杂度一样,当用邻接矩阵存储时,时间复杂度为O(n^2);采用邻接表存储的时候,时间复杂度是O(n+e),两种遍历的不同在于对顶点的访问顺序不同

图的应用

最小生成树,各边代价之和最小的那颗生成树

prim算法——加点法

时间复杂度O(n^2),与边数无关,空间复杂度O(n),适用于求稠密图的最小生成树

kruscal算法——加边法

时间复杂度O(elog2e),与边数有关,适用于求稀疏图的最小生成树

最短路径

Dijkstra算法

时间复杂度O(n^2)

Floyd算法

时间复杂度O(n^3),空间复杂度O(n^2)

拓扑排序

AOV-网,以顶点表示活动,用弧表示活动间的优先关系的有向图

选择无前驱的顶点,删除该顶点以及与它有联系的弧,在剩下的节点重复操作,输出的顶点如果少于总顶点数,则说明有环,否则没有环

对于给出的AOV网判断网中是否存在环,对有向图的所有顶点进行拓扑排序,如果所有的顶点都在拓扑序列中,那么不存在环

时间复杂度O(n+e)

关键路径,源点到汇点的带权路径长度最长的路径

AOE-网,以边表示活动的网

时间复杂度O(n+e)

查找(记忆各个查找算法的特点)

顺序查找

折半查找

分块查找

二叉排序树查找

优点

对表结构没有要求,使用顺序,也使用链式

比较次数少,查找效率高

在表中进行插删操作时,只需要找到对应的块就可以在块内进行插删操作,如果线性表经常动态变化,又需要快速查找,则使用分块查找

对于经常插入,删除,和查找运算的表,采用二叉排序树更好

缺点

平均查找长度较大,查找效率低

对结构要求高,只能适用于顺序存储结构的有序表,不适用数据元素经常变动的线性表

要增加一个索引表的存储空间并对初始索引表进行排序运算

平均查找长度

时间复杂度

(n+1)/2

O(n)

最坏情况查找不超过log2n+1

与二叉排序树的一样

O(log2n)

1/n * (每层个数*该层层数)

O(log2n)

顺序查找

平均查找长度ASL = 1/n * (n*(n+1)/2),时间复杂度O(n)

优点

对表结构没有要求,使用顺序,也使用链式

缺点

平均查找长度较大,查找效率低

折半查找

要求线性表必须采用顺序存储结构,最好情况查找1次,最坏情况查找log2(n)+1次,平均查找长度ASL=log2(n+1)-1

优点

比较次数少,查找效率高

缺点

对结构要求高,只能适用于顺序存储结构的有序表,不适用数据元素经常变动的线性表

分块查找

优点

在表中进行插删操作时,只需要找到对应的块就可以在块内进行插删操作,如果线性表经常动态变化,又需要快速查找,则使用分块查找

缺点

要增加一个索引表的存储空间并对初始索引表进行排序运算

二叉排序树的查找

含有n个节点的二叉排序树的平均查找长度和树的形态有关,最坏情况二叉树的形态为单分支形态,O(log2n)

ASL = 1/n*(1+2+...+n)

对于经常插入,删除,和查找运算的表,采用二叉排序树更好

void SearchBST(BSTree T, KeyType key){

if(!T || key==T->data.key) return T;

else if(key<T->data.key) return SearchBST(T->lchild,key);

else return SearchBST(T->rchild,key);

}

二叉排序树的插入

void InsertBST(BSTree &T, ElemType e){

if(!T){

s = new BSTNode;

s->data = e;

s->lchild=NULL, s->rchild = NULL;

T = s;

}

else if(e.key<T->data.key) return InsertBST(T->lchild,e);

else if(e.key>T->data.key) return InsetBST(T->rchild,e);

}

时间复杂度同查找一样,O(log2n)

二叉排序树的创建

void CreateBST(BSTree &T){

//依次读入关键字为key的节点,把相应的节点插入到二叉排序树中

T= NULL;

cin>>e;

while(e.key!='#'){//输入结束标志

InsetBST(T,e);

cin>>e;

}

}

插入一个节点的时间复杂度是O(log2n),n个节点的时间复杂度是O(nlog2n)

二叉排序树的删除

基本过程也是查找,所以时间复杂度是O(log2n),采用的过程是找到待删节点的直接前驱节点p,将p代替待删节点,将p的左子树接到p的双亲的右子树上面

平衡二叉树,AVL树

左子树与右子树的深度之差绝对值不超过1;左子树与右子树也是平衡二叉树

深度与log2n是同一数量级,所以查找的时间复杂度O(log2n)

散列表查找法,无需作比较或做很少比较,按照这种关系直接由关键字找到相对应的记录

散列函数和散列地址,在记录的存储地址p和关键字key之间建立一个确定的对应关系H,使得p=H(key),称这个对应关系H是散列函数,p是散列地址

散列表,一个连续的地址空间,用以存储按照散列函数计算得到的相应的散列地址的数据记录

冲突与同义词,对不同的关键字可能得到同一个散列地址,这种现象叫做冲突;具有相同函数值的关键字对该散列函数而言是同义词

处理冲突的方法,开放地址法,链地址法

散列函数的构造方法,考虑因素

散列表的长度

关键字的长度

关键字的分布情况

计算散列函数所需的时间

记录的查找频率

装填因子α越小,冲突发生频率越小,反之越大;散列函数均匀的情况下,影响平均查找长度的因素——处理冲突的方法和装填因子α,而不是待存放元素个数

散列表平均查找长度

插入排序(看后面的对比)

直接插入排序,时间复杂度O(n^2),空间复杂度,需要借助一个监视哨,O(1)

算法稳定、简单,适用于链式存储结构,顺序存储结构;更适用于初始记录基本有序的情况,初始记录无序,n较大时,则不适用

void InsertSort(SqList &L){

for(i=2;i<=L.length;i++){

if(L.r[i].key<L.r[i-1].key) {

L.r[0].key = L.r[i].key;

L.r[i] = L.r[i-1];

for(j=i-2; L.r[0].key<L.r[j].key ;j--){

L.r[j+1] = L.r[j];

}

L.r[j+1] = L.r[0];//比较到比插入的值还要小的数时,又往前面走了一个单位,所以要j+1

}   

}

}

折半插入排序

时间复杂度O(n^2),空间复杂度O(1);只适用于顺序结构,不适用链式结构,适用于初始无序,n值较大的情况

void BInsertSort(SqList &L){

for(i = 2;i<=L.length;i++){

L.r[0] = L.r[i];

low = 1, high = i-1;

while(low<=high){

m = (low+high)/2;

if(L.r[0].key<L.r[m].key) high = m-1;

else low = m+1;

}

for(j=i-1;j>=high+1;j--) L.r[j+1] = L.r[j];//后移

L.r[high+1]  = L.r[0];//替换

}

}

希尔排序

记录跳跃式地移动导致排序是不稳定的,只能适用于顺序结构,不能使用链式结构,适用于初始记录无序,n值较大的情况

冒泡排序

void BubbleSort(SqList &L){

m = L.length-1,flag = 1;

while(m>0 && flag==1){

flag = 0;//默认本趟没有发生了交换

for(j = 1;j<=L.length;j++){

if(L.r[j+1]<L.r[j]){

flag = 1;//发生了交换做的标志

temp = L.r[j+1];

L.r[j+1] = L.r[j];

L.r[j] = temp;

}

}

m--;

}

}

时间复杂度O(n^2),空间复杂度O(1);稳定排序,同样适用于链式结构,移动次数多,比直接插入排序差;不适用初始无序,n值较大的情况

快速排序

int Partition(SqList &L, int low, int high){

L.r[0] = L.r[low];

pivotkey = L.r[low].key;

while(low<high){

while(low<high && L.r[high].key>=pivotkey) --high;//从右往左开始,目的是寻找比枢轴变量更小的量

L.r[low] = L.r[high];

while(low<high && L.r[low].key<=pivotkey) low++;//从左往右开始,目的是寻找比枢轴变量更大的量

L.r[high] = L.r[low];

}

//枢轴变量到位

L.r[low] =L.r[0];

return low;

}

void QSort(SqList &L, int low, int high){

//调整low=1, high = L.length

if(low<high){

pivotloc = Partition(L,low,high);

QSort(L,low,pivotloc-1);

QSort(L,pivotloc+1,high);

}

}

void QuickSort(SqList &L){

QSort(L,1,L.length);

}

时间复杂度O(nlog2n),空间复杂度,因为调用是递归的,需要使用工作栈,O(n);适用于顺序结构,很难适用链式结构、适用初始记录无序,n较大的情况

选择排序、一次选择最小的记录

void SelectSort(SqList &L){

for(i=1;i<L.length;i++){

k =i;

for(j =i+1;j<=L.length;j++){

if(L.r[j].key<L.r[k].key) k = j;

}

if(k!=i){

temp = L.r[i];

L.r[i] = L.r[k];

L.r[k] = temp;

}

}

}

时间复杂度O(n^2),空间复杂度O(1)、算法不稳定、可适用于链式结构、顺序结构

堆排序

不稳定排序,只适用顺序结构,不适用链式结构;

二路归并排序

时间复杂度O(nlog2n),空间复杂度O(n),是稳定排序,适用顺序结构、链式结构,工作时仍然需要开辟工作栈

(要掌握每个算法走一趟后的序列,不需要掌握具体的算法,自己会一两个就行,重要的是排序算法的过程,还有各自的特性)

直接插入排序

时间复杂度O(n^2),空间复杂度O(1)

适用于初始记录有序的情况,不适用初始记录无序,n较大的情况

适用顺序结构,也适用链式结构

稳定

折半插入排序

时间复杂度O(n^2),空间复杂度O(1);适用初始无序,n较大的情况;适用顺序结构,不适用链式结构

稳定

希尔排序

时间复杂度O(n^1.3),空间复杂度O(1)

适用于初始记录无序,n较大的情况

只适用顺序结构,不适用链式结构

不稳定

冒泡排序

时间复杂度O(n^2),空间复杂度O(1)

不适用初始记录有无序,n较大的情况

适用顺序结构、链式结构

稳定

快速排序

时间复杂度O(nlog2n),空间复杂度O(n)

适用初始记录无序,n较大的情况

适用顺序结构,很难适用链式结构

不稳定

选择排序

时间复杂度O(n^2),空间复杂度O(1)

适用顺序结构、链式结构

稳定

堆排序

时间复杂度O(nlog2n),空间复杂度O(1)

适用于n较大的情况

只能使用顺序结构,不适用链式结构

不稳定

二路归并排序

时间复杂度O(nlog2n),空间复杂度O(n),需要借助工作栈

适用顺序结构、链式结构

稳定

 

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 期末复习数据结构讲义pdf是指一份包含数据结构相关知识的教材或讲义的电子文档。 首先,这份讲义是为期末考试而准备的,因此它会涵盖这门课程所讲授的数据结构的主要概念和内容。它通常会包括各种数据结构的定义、特性、操作等内容,例如数组、链表、栈、队列、树、图等等。此外,这份讲义还可能包括一些与数据结构相关的算法和问题,如排序算法、查找算法、遍历算法等。 其次,这份讲义的目的是帮助学生复习数据结构的知识,提供一个系统化的学习资料。因此,它通常会按照逻辑顺序组织,从基础的概念开始,逐渐深入,直至较为复杂的内容。学生可以通过研读这份讲义,回顾和巩固课堂上所学的内容,理解数据结构的原理和应用。 此外,这份讲义可能会包含一些实例和习题,以帮助学生加深对数据结构知识的理解和应用能力。这些实例和习题可以用来训练学生解决实际问题的能力,并提供一些思路和方法。 最后,这份讲义以pdf格式呈现,具有电子化的特点。学生可以方便地通过电脑、平板电脑或手机等设备随时随地查阅,并进行标注、批注等操作,方便复习和学习。 总之,期末复习数据结构讲义pdf是一份针对数据结构课程的复习资料,通过系统、全面地总结了数据结构的相关知识,帮助学生进行复习,并提供了一些实例和习题,方便学生加深对数据结构的理解和应用。 ### 回答2: 期末复习数据结构讲义pdf是一份非常重要的学习资料。在期末考试前进行复习时,它可以作为一个很好的参考工具。 首先,数据结构是计算机科学中的一门核心课程,涉及到很多基础的知识和概念。这份讲义中记录了数据结构的各种基本概念、定义和性质,以及常见的数据结构类型,如数组、链表、栈、队列、树等。对于复习阶段来说,这些内容对于回顾和加深理解非常有帮助。 此外,这份讲义还包括了数据结构的一些重要算法和操作,例如查找、排序和插入等。这些算法数据结构中的关键,理解它们的原理和实现方式对于提代码效率和解决实际问题至关重要。 最后,这份讲义可能还包括一些实例或编程题目,供学生进行练习和巩固应用知识。这是非常有用的,因为通过实际操作和编程实践,学生可以更深入地理解数据结构的概念和应用,并提升自己的编程能力。 总之,期末复习数据结构讲义pdf是一份极其重要的学习资料,它汇集了数据结构的基本概念、算法和实例,为学生提供了一个全面深入的复习和巩固知识的工具。我们应该认真阅读、理解和应用这份讲义,希望能够在期末考试中取得优异的成绩。 ### 回答3: 数据结构是计算机科学中的重要基础课程,掌握数据结构对于学习和应用计算机算法具有至关重要的作用。期末复习数据结构讲义PDF是一种非常有效的学习资料。以下是对该讲义的回答: 期末复习数据结构讲义PDF对学习数据结构有很大帮助。首先,该讲义系统地总结了各种常见的数据结构及其应用,如链表、栈、队列、二叉树、图等。通过讲义中的讲解和示例,可以清晰地了解每种数据结构的定义、特点和操作。 其次,该讲义提供了大量的例题和练习题,能够帮助学生巩固对数据结构的理论知识和运用能力。通过讲义中的习题,学生可以对所学知识进行实际的应用,深化对数据结构的理解,并培养解决实际问题的能力。 此外,该讲义还包含了一些常见算法的讲解,如排序、查找、图的遍历等。这些算法数据结构密切相关,掌握这些算法能够提升学生的算法设计和分析能力。 最后,该讲义的PDF格式方便学生进行随时随地的学习。学生可以通过电脑、平板或手机等设备随时打开讲义进行学习,非常方便。 综上所述,期末复习数据结构讲义PDF具有很的教学价值。它能够帮助学生系统地学习和巩固数据结构的知识,提算法设计和分析能力。同时,讲义的PDF格式也很方便学生进行学习。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值