数据结构期末复习+代码

数据结构

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

时间复杂度,logn

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

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

顺序表的初始化

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

       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.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

       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

       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

       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

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

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<

}

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<

}

}

括号匹配

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

上三角

三角矩阵,从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

下三角矩阵

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

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

计算规则(牢记)根据节点数计算二叉树有多少种=(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<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<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

cin>>G.vexs[i];

}

for(i=0;i

for(j=0;j

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

}

}

for(k=0;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<

for(w = 0;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<

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<

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<

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(keydata.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.keydata.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[0].key = L.r[i].key;

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

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

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

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]

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

while(low=pivotkey) --high;//从右往左开始,目的是寻找比枢轴变量更小的量

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

while(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

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

k =i;

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

if(L.r[j].key

}

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),需要借助工作栈

适用顺序结构、链式结构

稳定

 

你可能感兴趣的:(数据结构,数据结构,算法,java)