数据结构强化小结

1. 时间复杂度

链表合并

最坏情况下

t(n) = m+n-1

O(n) = O(max(m,n))

2. 数据结构

2.1 线性结构

2.1.1 线性表

  • 顺序表(顺序存储、随机存取)
#define MaxSize 500 //宏定义

//定义
typedef struct{
    ElemType *elem; //基地址
    int length; //表长
}Sqlist;

//初始化
bool InitList(Sqlist &L){
    L.elem = new ElemType[MaxSize]; //L.elem = (ElemType*)malloc(sizeof(ElemType)*InitSize);
    if(!L.elem) return false;
    L.length = 0;
    return true;
}

//取值 O(1)
bool GetElem(SqList L,int i,ElemType e){
    if(i<1 || i>L.length) return false;
    e = L.elem[i-1];
    return true;
}

//查找 O(n)
int LocateElem(SqList L,ElemType e){
    for(int i = 0;i < L.length;i++){
        if(L.elem[i] == e) return i+1;
    }
    return 0;
}

//插入 O(n)
bool ListInsert(SqList &L,int i,ElemType e){
    if(i<1 || i>L.length+1) return false;
    if(L.length == MaxSize) return false;
    for(int j = L.length-1;j>=i-1;j--){
        L.elem[j+1] = L.elem[j];
    }
    L.elem[i-1] = e;
    L.length++;
    return true;
}

//删除
bool ListDelete(SqList &L,int i){
    if(i<1 || i>L.length) return false;
    for(int j = i;j < L.length;j++{
        L.elem[j-1] == L.elem[j];
    )
    L.length--;
    return true;
}

/*注意;
  &取址
  *取值
 */
  • 链表

    • 单链表
    //定义
    typedef struct LNode{
        ElemType data;
        Struct LNode *next; 
    }LNode,*LinkList
    
    //初始化
    bool InitList(LinkList L){
        L = new LNode;
        L->next = null;
        return true;
    }
    
    //取值
    bool GetElem(LinkList L,int i,ElemType &e){
        LNode *p = L->next;
        int j = 1;
        while(p && j<i){
            p = p->next;
            j++;
        }
        e = p->data;
        if(!p) return false;
        return true;
    }
    
    //查找
    LNode *LocateElem(LinkList L,ElemType e){
        LNode *p = L->next;
        while(p && p->next!=e)
            p = p->next;
        return p;
    }
    
    //插入
    s = new LNode;
    s->data = e;
    s->next = p->next;
    p->next = s;
    
    //删除
    q = p->next;
    p->next = q->next;
    free(q);    // delete(q);
    
    //创建单链表
    //头插法
    L = new LNode;
    L->next = null;
    for(int i = 0;i<n;i++){
        s = new LNode;
        s->data = x;
        s->next = L->next;
        L->next = s;
    }
    //尾插法
    L = new LNode;
    L->next = null;
    r = L;
    for(int i = 0;i<n;i++){
        s = new LNode;
        s->data = x;    //cin>>s->data;
        s->next = null;
        r->next = s;
        r = s;
    }
    
    • 双链表
    typedef struct DLNode{
        ElemType data;
        struct DLNode *prior;
        struct DLNode *next;
    }DLNode,*DLinkList;
    
    • 循环链表

    • 静态链表

    #define MaxSize 500
    typedef struct{
        ElemType data;
        int next;
    }SLinkList[MaxSize];
    

2.1.2 栈

  • 顺序栈
typedef struct{
    ElemType data[MaxSize];
    int top;
}SqStack;

//初始化
bool InitStack(SqStack &S){
    S.top = -1;
    return true;
}

//判栈空
bool StackEmpty(SqStack S){
    if(S.top == -1) return true;
    return false;
}

//入栈
bool Push(SqStack &S,ElemType e){
    if(top == MaxSize-1) return false;
    S.top++;
    S.data[S.top] = e;
    return true;
}

//出栈
bool Pop(SqStack &S,ElemType &e){
    if(S.top == -1) return false;
    e = S.data[S.top];
    S.top--;
    return true;
}

//读栈顶元素
bool GetTop(SqStack S,ElemType &e){
    if(S.top == -1) return false;
    e = S.data[S.top];
    return true;
}
  • 链栈
typedef struct StackNode{
    ElemType data;
    struct StackNode *next;
}StackNode,*LinkStack;

//初始化
bool InitStack(LinkStack &S){
    S = null;
    return true;
}

//入栈
bool Push(LinkStack &S,ElemType e){
    StackNode *p;
    p = new StackNode;
    p->data = e;
    p->next = S;
    S = p;
    return true;
}

//出栈
bool Pop(LinkStack &S,ElemType e){
    if(S == null) return false;
    e = S->data;
    StackNode *p;
    p = S;
    S = S->next;
    delete p;
    return true;
}
  • 共享栈

2.1.3 队列

  • 顺序队列
#define MaxSize 500
typedef struct{
    ElemType data[MaxSize];
    int rear,front;
}SqQueue;

//初始化
bool InitQueue(SqQueue &Q){
    Q.front = Q.rear = 0;
    return true;
}

//求队列长度
int QueueLength(SqQueue Q){
    return (Q.rear-Q.front + MaxSize)%MaxSize;
}

//入队
bool EnQueue(SqQueue &Q,ElemType e){
    if((Q.rear+1)%MaxSize == front) return false;
    Q.data[Q.rear] = e;
    Q.rear = (Q.rear+1)%MaxSize;
    return true;
}

//出队
bool DeQueue(SqQueue &Q,ElemType &e){
    if(Q.front == Q.rear) return false;
    e = Q.data[Q.front];
    Q.front = (Q.front+1)%MaxSize;
    return true;
}
  • 链式队列

  • 循环队列

2.1.4 串

2.2 非线性结构

2.2.1 树

结点数=边数+1    结点数=度数之和+1

满二叉树 第i层 2^(i-1)个结点

h层满二叉树 共有2^h-1个结点

结点数n = n0+n1+n2+...+nm = 1 n1+2 n2+...+m nm+1

二叉树 n0 = n2+1

完全二叉树 n1=0 或 n1=1

线索二叉树

存储:双亲表示法、孩子表示法、孩子兄弟表示法

2.2.2 图

完全图    无向图有n(n-1)/2条边    有向图有n(n-1)条弧

生成子图    V'=V    E'包含于E

极大连通子图    极小连通子图

生成树    包含全部顶点的极小连通子图    边数=顶点数-1

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

存储:邻接矩阵法、邻接表法、十字链表法、邻接多重法

2.2.3 集合

//初始化
void Initial(int *set,int n){
    for(int i=0;i<n;i++){
        set[i] = -1;
    }
}

//查找
int find(int *set,int x){
    while(set[x]>0){
        x = set[x];
    }
    return x;
}

//合并
void merge(int set[],int x,int y){
    x = find(set,x);
    y = find(set,y);
    set[x] += set[y]; 
    set[y] = x;
}

3. 查找

3.1 顺序查找

平均查找长度ASL:成功(1+n)/2、失败n

3.2 折半查找

画出折半查找树,层数对应查找次数

ASL:成功每层结点数乘以层数求和后/n、失败每层失败结点个数乘以父结点所在层数求和后/失败结点总个数

注意:失败结点数=成功结点数n+1

3.3 BST

二叉排序树、二叉搜索树

中序遍历(左-根-右)得到有序序列

删除操作:替换为直接前驱或直接后驱之后到了叶子结点的位置,直接删除叶子结点

3.4 AVL

二叉平衡树:左右子树高度差小于等于1

Nh = Nh-1 + Nh-2 + 1(高度为h的平衡二叉树,最少需要Nh个结点)

(1)插入

LL:右旋    RR:左旋    LR:先左旋再右旋    RL:先右旋再左旋

(2)删除

叶子结点删除后是AVL直接删除,删除后不是AVL进行旋转

被删除结点只有左子树/右子树,删除后用左子树/右子树直接代替,调整至AVL

被删除结点既有左子树又有右子树,平衡因子为1(-1),用直接前驱(直接后驱)代替后删除,调整至AVL

3.5 红黑树

新插入结点默认为红色

空树或父黑:红色

父红且叔红:叔父染色,祖父染色

父红且叔黑/空:LL:父、祖父染色,祖父右旋;LR:父左旋,转换为LL型,父、祖父染色,祖父右旋;RR:父、祖父染色,祖父左旋;RL:父右旋,转换为RR型,父、祖父染色,祖父左旋

3.6 B树/B+树

m阶B树最多有m个子结点(m个分支)。除根和叶子外,每个结点至少有m/2向上取整个子结点。所有叶子结点都在同一层。关键字个数不超过m-1

n个关键字对应n+1个子结点

B+树支持顺序查找和随机查找,B树只支持随机查找

(1)插入

插入后关键字个数不超过m-1,直接插入

插入后关键字个数超过m-1,取关键字中间值放入父结点

(2)删除

被删关键字不在终端结点,用其直接前驱或直接后驱代替,转换为被删关键字在终端结点的情况

被删关键字在终端结点,该结点关键字个数大于等于m/2向上取整,直接删除;该结点关键字个数等于m/2向上取整 - 1且与其相邻的结点关键字个数大于等于m/2向上取整,父子换位;该结点关键字个数等于m/2向上取整 - 1且与其相邻的结点关键字个数等于m/2向上取整 -1,父子合并

3.7 散列表

堆积/聚集:线性探测法中,冲突的数据连成一大片连续区域

冲突:不同的关键字映射到同一位置(同义词)

装填因子 = 关键字个数 / 表长

产生冲突的原因呢:散列函数、处理冲突的方法、装填因子

查找成功的平均查找长度ASL:所有关键字探测次数之和 / 关键字个数

查找失败的平均查找长度ASL:函数值域内的每个地址的失败探测次数之和 / 可映射到的地址数

4. 排序

4.1 插入排序

4.1.1 直接插入排序

void InsertSort(ElemType A[],int n){
    int i,j;
    for(i=2;i<=n;i++){
        if(A[i]<A[i-1]){    //有序和无序的分界处
            A[0]=A[i];    //第一个无序的数字赋值给哨兵
            for(j=i-1;A[0]<A[j];j--){    //将其插入至有序位
                A[j+1] = A[j];
            }
            A[j+1]=A[0];
        }
    }
}

Best:数据已有序,比较n-1次,移动0次。O(n)

Worst:数据逆序,
9b5cb12d-f8c9-4b07-8845-88ff55601868

4.1.2 折半插入排序

void InsertSort(ElemType A[],int n){
    int i,j,low,high,mid;
    for(i=2;i<=n;i++){
        A[0] = A[i];
        low = 1;
        high = i - 1;
        while(low<=high){    //二分查找,新加入元素应插入位置
            mid = (low + high)/2;
            if(A[mid]>A[0]) high = mid - 1;
            else low = mid + 1;
        }
        for(j=i-1;j>=high+1;--j){    //将大于A[0]的值后移
            A[j+1] = A[j];
        }
        A[high+1] = A[0];    //使A[0]值赋值给A[high+1]使有序
    }
}

减少了比较元素的次数,约为O(n logn)

移动次数并未改变

4.1.3 希尔排序

先将原表分割成若干子表,对各个子表进行直接插入排序

void ShellSort(ElemType A[],int n){
    for(dk=n/2;dk>=1;dk=dk/2){    //分割,调整步长。增量序列为互质、递减。先粗调后微调
        for(i=dk+1;i<=n;++i){    //直接插入排序
            if(A[i]<A[i-dk]){
                A[0] = A[i];
                for(j=i-dk;j>0 && A[0]<A[j];j-=dk){
                    A[j+dk] = A[j];
                }
                A[j+dk] = A[0];
            }
        }
    }
}

4.2 交换排序

4.2.1 冒泡排序

void BubbleSort(ElemType A[],int n){
    for(int i=0;i<n-1;i++){    //i是有序和无序的分界线,i也是趟数
        bool flag = false;
        for(int j=n-1;j>i;j--){    //从后往前简单交换,将较小值冒泡到靠前位置
            if(A[j-1]>A[j]){
                swap(A[j-1],A[j]);
                flag = true;
            }
        }
        if(flag == false)    //某趟结束后没有交换,则已排序完成
            return;
    }
}

Best:数据已有序,比较n-1次,移动0次。O(n)

Worst:数据逆序,每趟比较(n-趟数)次,每比较1次移动3次。O(n平方)

4.2.2 快速排序

void QuickSort(ElemType A[],int low,int high){
    if(low<high){
        int pivotpos = Partion(A,low,high);    //根据枢轴元素将原表分割成两个独立的子表
        QuickSort(A,low,pivotpos-1);    //递归排序
        QuickSort(A,pivotpos+1,high);
    }
}

int Partion(ElemType A[],int low,int high){    //使枢轴元素移动到最终的位置
    ElemType pivot = A[low];
    while(low<high){
        while(low<high && A[high]>=pivot)    --high;
        A[low] = A[high];
        while(low<high && A[low]<=pivot)    ++low;
        A[high] = A[low];
    }
    A[low] = pivot;
    return low;
}

Best:每一趟排序后都能将元素序列均匀分割或两个长度大致相等的子表

4.3 选择排序

4.3.1 简单选择排序

void SelectSort(ElemType A[],int n){
    for(i=0;i<n-1;i++){
        min = i;
        for(j=i+1;j<n;j++){
            if(A[j]<A[min])
                min = j;
        }
        if(min!=i)
            swap(A[i],A[min]);
    }
}

Best:不移动元素

Worst:移动3(n-1)次

无论初始排列如何,所需比较次数均为
94e9f8b9-d69c-493e-afbc-8d7d316690b1

4.3.2 堆排序

大根堆:L(i)>=L(2i)且L(i)>=L(2i+1)

小根堆:L(i)<=L(2i)且L(i)<=L(2i+1)

void BuildMaxHeap(ElemType A[],int len){
    for(int i=len/2;i>0;i++){
        HeapAdjust(A,i,len);
    }
}

void HeapAdjust(ElemType A[],int k,int len){
    A[0] = A[k];
    for(i=2*k;i<=len;i*=2){
        if(i<len && A[i]<A[i+1])
            i++;
        else{
            A[k] = A[i];
            k = i;
        }      
    }
    A[k] = A[0];
}

void HeapSort(ElemType A[],int len){
    BuildMaxHeap(A,len);
    for(int i=len;i>1;i--){
        swap(A[i],A[1]);
        HeapAdjust(A,1,i-1);
    }
}

4.4 归并排序

ElemType *B = (ElemType*)malloc((n+1)*sizeof(ElemType));

void Merge(ElemType A[],int low,int mid,int high){    //二路归并
    for(int k=low;k<=high;k++){
        B[k] = A[k];
    }
    for(i=low,j=mid+1,k=i;i<=mid && j<=high;k++){
        if(B[i]<=B[j])
            A[k] = B[i++];
        else
            A[k] = B[j++];
    }
    while(i<=mid){
        A[k++] = B[i++];
    }
    while(j<=high){
        A[k++] = B[j++];
    }
}

void MergeSort(ElemType A[],int low,int high){
    if(low<high){
        int mid = (low+high)/2;    //递归分层
        MergeSort(A,low,mid);
        MergeSort(A,mid+1,high);
        Merge(A,low,mid,high);    //合并
    }
}

时间复杂度O(n logn)

4.5 基数排序

最高位优先法

最低位优先法

4.6 外部排序

生成初始归并段+多路归并(s趟归并排序)

优化:增大归并路数k,减少初始归并段个数r

败者树

置换选择排序

最佳归并树:IO次数=2*WPL。(n0-1)%(k-1)=0 正好、=u 要补 k-u-1 个虚段

4.7 小结

比较次数与初始序列有关:快排、直接插入、冒泡、堆、希尔

比较次数与初始序列无关:归并、简单选择、基数

排序趟数与初始序列有关:冒泡、快排

排序趟数与初始序列无关:直接插入、折半插入、希尔、简单选择、归并、基数

不稳定排序:选择、快排、希尔、堆

5. 应用

5.1 并查集的应用

最小生成树、求无向图的连通性、求无向图的连通分量

压缩路径:优化Find操作,先找到根节点,再将查找路径上的所有结点都挂到根结点上

int Find(int s[],int x){
    int root = x;
    while(s[root]>=0){
        root = s[root];
    }    //此时root为0
    while(x!=root){
        int t = s[x];
        s[x] = root;
        x = t;
    }
    return root;
}

5.2 哈夫曼树/哈夫曼编码

n个关键字构造的哈夫曼树包含的结点总数为2n-1

n0 = n2 + 1

WPL = 非根节点权值之和

5.3 最小生成树

包含全部顶点的一个极小连通图,具有最小权值

Prim(适合边稠密图):任选一顶点,选取距离顶点集合最近的顶点加入生成树

Kruskal(适合边稀疏而顶点多的图):保证不连通的前提下,选取权值最小的边加入生成树

5.4 最短路径

BFS

Dijkstra

Floyd

5.5 拓扑排序

拓扑序列

5.6 关键路径

ve(事件最早开始时间)、vl(事件最迟开始时间)、e(活动最早开始时间)、l(活动最迟开始时间)

活动的时间余量:vl - ve - l 。

5.7  矩阵压缩

6. 遍历

6.1 树的遍历

//递归
void InOrderTraverse(BiTree T){    //中序遍历
    if(T){
        InOrderTraverse(T->lchild);
        cout<<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,p);
            cout<<q->data;
            p = q->rchild;
        }
    }
}

6.2 DFS 深度遍历

bool visited[MVNum];

void DFS(Graph G,int v){
    cout<<v;
    visited[v] = true;
    for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)){
        if(!visited[w]){
            DFS(G,w);
        }
    }
}

//采用邻接矩阵 O(n方)
void DFS_AM(AMGraph G,int v){
    cout<<v;
    visited[v] = true;
    for(w=0;w<G.vexnum;w++){
        if(G.arcs[v][w]!=0 && (!visited[w])){
            DFS_AM(G,W);
        }
    }
}

//采用邻接表 O(n+e)
void DFS_AL(ALGraph G,int v){
    cout<<v;
    visited[v] = true;
    p = G.vertices[v].firstarc;
    while(p!=null){
        w = p->adjvex;
        if(!visited[w]){
            DFS_AL(G,W);
        }
        p = p->nextarc;
    }
}

6.3 BFS 广度遍历

void BFS(Graph G,int v){
    cout<<v;
    visited[v] = true;
    InitQueue(Q);
    EnQueue(Q,v);
    while(!QueueEmpty(Q)){
        DeQueue(Q,u);
        for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w)){
            if(!visited[w]){
                cout<<w;
                visited[w] = true;
                EnQueue(Q,w);
            }
        }
    }
}
posted on 2026-09-01 21:42  神奈酱  阅读(6)  评论(0)    收藏  举报