zoukankan      html  css  js  c++  java
  • 第3章 顺序表的链式存储

    第3章 顺序表的链式存储

    数据结构与算法_师大完整教程目录(更有python、go、pytorch、tensorflow、爬虫、人工智能教学等着你):https://www.cnblogs.com/nickchen121/p/13768298.html

    一、链式存储

    1. 解决问题:对于线性结构,使用顺序存储,需要足够大的连续存储区域
    2. 链式存储:结点除了存放信息,并且附设指针,用指针体现结点之间的逻辑关系
    3. 注:(c)语言的动态分配函数(malloc())(free())分别实现内存空间的动态分配和回收,所以不必知道某个结点的具体地址
    4. 注:链式存储中,必须有一个指针指向第一个结点的存储位置,一般为(head)标示
    5. 顺序存储和链式存储的区别:顺序存储更适合查询量大的程序设计;链式存储更适合需要频繁插入和删除的程序

    二、单链表

    2.1 单链表的基本概念及描述

    1. 单链表结点构造:两个域,一个存放数据信息的(info)域;另一个指向该结点的后继结点的(next)

    2.2 单链表的实现

    1. 单链表的常用操作:

      1. 建立一个空的单链表
      2. 输出单链表中各个结点的值
      3. 在单链表中查找第(i)个结点

    2.2.1 单链表的存储结构

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    

    2.2.2 单链表的插入操作(算法)

    1. 算法步骤(插入结点为(p),插入到结点(q)后面):

      1. 通过 find(head,i) 查找(q)结点,查不到打印报错信息
      2. 给插入结点(p)分配空间,并设置信息
      3. 如果在单链表的最前面插入新结点,让单链表的首指针指向新插入的结点

    p->next = head;
    head = p;
    6. 如果在单链表中间插入新结点:

    p->next = q->next;
    q->next=p;

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    
    node *insert(node *head, datatype x, int i) {
        node *p, *q;
        q = find(head, i); // 查找第i个结点
        if (!q && i != 0) {
            printf("
    找不到第%d个结点,不能插入%d!", i, x);
        } else {
            p = (node *) malloc(sizeof(node)); // 分配空间
            p->info = x; // 设置新结点
            if (i == 0) // 插入的结点作为单链表的第一个结点
            {
                p->next = head;
                head = p;
            } else {
                p->next = q->next; // 后插
                q->next = p;
            }
        }
        return head;
    }
    

    2.2.3 单链表的删除操作(算法)

    1. 算法步骤(被删除结点(q),被删除结点前一个结点(pre)

      1. 判断链表是否为空
      2. 循环查找被删除结点(q),并且设置一个结点(pre)标示被删除结点的前一个结点
      3. 如果删除结点为第一个结点

    head = head->next;
    free(p)
    6. 如果删除结点为其他结点

    pre->next = q->next;
    free(p)

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    
    node *dele(node *head, datatype x) {
        node *pre = NULL, *p;
        if (!head) {
            printf("单链表是空的");
            return head;
        }
        p = head;
        while (p && p->info != x) // 寻找被删除结点p
        {
            pre = p; // pre指向p的前驱结点
            p = p->next;
        }
        if (p) {
            if (!pre) // 被删除结点没有上一个结点,则是要删除的是第一个结点
            {
                head = head->next;
            } else {
                pre->next = p->next;
            }
            free(p)
        }
        return head;
    }
    

    三、带头结点的单链表

    3.1 带头结点的单链表的基本概念及描述

    1. 头结点的作用:单链表的插入和删除需要对空的单链表进行特殊处理,因此可以设置 (head) 指针指向一个永远不会被删除的结点——头结点
    2. 注:(head) 指示的是所谓的头结点,它不是实际结点,第一个实际结点应该是 head->next 指示的

    3.2 带头结点的单链表的实现

    1. 带头结点的单链表的常用操作:

      1. 建立一个空的带头结点的单链表
      2. 输出带头结点的单链表中各个结点的值
      3. 在带头结点的单链表中查找第 (i) 个结点

    3.2.1 带头结点的单链表的存储结构

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    

    3.2.2 带头结点的单链表的插入(算法)

    1. 算法步骤( (p) 为插入结点,(q) 为插入前一个结点):

      1. 通过 find(head,i) 查找带头结点的单链表中的第 (i) 个结点( (i=0) 表示新结点插入在头结点之后)
      2. 如果没找到结点 (q),打印报错信息
      3. 如果在非空的带头结点的单链表最前面插入一个新结点

    p->next = q->next;
    q->next = p;
    6. 如果在非空的带头结点的单链表的内部插入一个新结点

    p->next = q->next;
    q->next = p;

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    
    node *insert(node *head, datatype x, int i) {
        node *p, *q;
    
        q = find(head, i); // 查找带头结点的单链表中的第 i 个结点,i=0 时表示新结点插入在头结点之后
    
        if (!q) // 没有找到
        {
            printf("
    带头结点的单链表中不存在第%d个结点!不能插入%d!", i, x);
            return head;
        }
    
        p = (node *) malloc(sizeof(node)); // 为准备插入的新结点分配空间
        p->info = x; // 为新结点设置值
        p->next = q->next;
        q->next = q; // i=0 时,本语句等价于 head->next=p
        return head;
    }
    

    3.2.3 带头结点的单链表的删除(算法)

    1. 算法步骤(被删除结点为 (q),被删除结点的前一个结点为 (pre)):

      1. 设置 (pre) 指向头结点
      2. (q) 从带头结点的单链表的第一个实际结点开始循环寻找值为 (x) 的结点
      3. 删除带头结点的单链表的第一个实际结点:

    pre->next = q->next;
    free(q)
    6. 删除带头结点的单链表的内部结点:

    pre->next = q->next;
    free(q)

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    
    node *dele(node *head, datatype x) {
        node *pre = head, *q; // pre 指向头结点
    
        q = head->next; // q 从带头结点的单链表的第一个实际结点开始找值为 x 的结点
        while (q && q->info != x) // 循环查找值为 x 的结点
        {
            pre = q; // pre 指向 q 的前驱
            q = q->next;
        }
    
        if (q) {
            pre->next = q->next; // 删除
            free(q); // 释放内存空间
        }
        return head;
    }
    

    四、循环单链表

    4.1 循环单链表的基本概念及描述

    1. 单链表存在的问题:从表中的某个结点开始,只能访问该结点后面的结点
    2. 循环单链表解决的问题:从表中的任意一个结点开始,使其都能访问到表中的所有的结点
    3. 循环单链表:在单链表的基础上,设置表中最后一个结点的指针域指向表中的第一个结点

    4.2 循环单链表的实现

    1. 循环单链表的常用操作:

      1. 建立一个空的循环单链表
      2. 获得循环单链表的最后一个结点的存储地址
      3. 输出循环单链表中各个结点的值
      4. 在循环单链表中查找一个值为 (x) 的结点
      5. 循环单链表的插入操作
      6. 循环单链表的删除操作
      7. 循环单链表的整体插入与删除操作

    4.2.1 循环单链表的存储结构

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    

    五、双链表

    5.1 双链表的基本概念及描述

    1. 双链表解决的问题:设置一个 (llink) 指针域,通过这个指针域直接找到每一个结点的前驱结点

    5.2 双链表的实现

    1. 双链表的常用操作:

      1. 建立一个空的双链表
      2. 输出双链表中各个结点的值
      3. 查找双链表中第 (i) 个结点
      4. 双链表的插入操作
      5. 双链表的删除操作

    5.2.1 双链表的存储结构

    typedef int datatype;
    typedef struct dlink_node {
        datatype info;
        struct dlink_node *llink, *rlink;
    } dnode;
    

    六、链式栈

    6.1 链式栈的基本概念及描述

    1. 链式栈:使用链式存储的栈
    2. 注:链式栈的栈顶指针一般用 (top) 表示

    6.2 链式栈的实现

    1. 链式栈的常用操作:

      1. 建立一个空的链式栈
      2. 判断链式栈是否为空
      3. 取得链式栈的栈顶结点值
      4. 输出链式栈中各个结点的值
      5. 向链式栈中插入一个值为 (x) 的结点
      6. 删除链式栈的栈顶节点

    6.2.1 链式栈的存储结构

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    

    七、链式队列

    7.1 链式队列的基本概念及描述

    1. 链式队列:使用链式存储的队列
    2. 注:队列必须有队首和队尾指针,因此增加一个结构类型,其中的两个指针域分别为队首和队尾指针

    7.2 链式队列的实现

    1. 链式队列的常用操作:

      1. 建立一个空的链式队列
      2. 判断链式队列是否为空
      3. 输出链式队列中各个结点的值
      4. 取得链式队列的队首结点值
      5. 向链式队列中插入一个值为 (x) 的结点
      6. 删除链式队列中的队首结点

    7.2.1 链式队列的存储结构

    typedef int datatype;
    typedef struct link_node {
        datatype info;
        struct link_node *next;
    } node;
    typedef struct {
        node *front, *rear; // 定义队首和队尾指针
    } queue;
    

    八、算法设计题

    8.1 求单链表中结点个数(算法)

    设计一个算法,求一个单链表中的结点个数

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    int count(linklist head) {
        int c = 0;
        linklist p = head; // head为实际的第一个结点
    
        while (p) // 计数
        {
            c++;
            p = p->next;
        }
        return c;
    }
    

    8.2 求带头结点的单链表中的结点个数(算法)

    设计一个算法,求一个带头结点单链表中的结点个数

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    int count(linlist head) {
        int c = 0;
        linklist = head->next; // head->next 为实际的第一个结点
    
        while (p) // 计数
        {
            c++;
            p = p->next;
        }
        return c;
    }
    

    8.3 在单链表中的某个结点前插一个新结点(算法)

    设计一个算法,在一个单链表中值为 y 的结点前面插入一个值为 x 的结点。即使值为 x 的新结点成为值为 y 的结点的前驱结点

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    void insert(linklist head, int y, int c) {
        linklist pre, p, s; // 假设单链表带头结点
        pre = head;
        p = head->next;
    
        while (p && p->data != y) {
            pre = p;
            p = p->next;
        }
    
        if (p) // 找到了值为 y 的结点,即 p == y
        {
            s = (linklist) malloc(sizeof(linknode));
            s->data = x;
            s->next = p;
            pre->next = s;
        }
    }
    

    8.4 判断单链表的各个结点是否有序(算法)

    设计一个算法,判断一个单链表中各个结点值是否有序

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    int issorted(linklist head, char c) // c='a' 时为升序,c='d' 时为降序
    {
        int flag = 1;
        linklist p = head->next;
    
        switch (c) {
            case 'a': // 判断带头结点的单链表 head 是否为升序
                while (p && p->next && flag) {
                    if (p->data <= p->next->data) p = p->next;
                    else flag = 0;
                }
                break;
            case 'd': // 判断带头结点的单链表 head 是否为降序
                while (p && p->next && flag) {
                    if (p->data >= p->next->data) p = p->next;
                    else flag = 0
                }
                break;
        }
        return flag;
    }
    

    8.5 逆转一个单链表(算法)

    设计一个算法,利用单链表原来的结点空间将一个单链表就地转置

    1. 核心思想:通过 head->next 保留上一个 (q) 的状态

    2. 算法步骤:

      1. (p) 指向实际的第一个结点
      2. 循环以下步骤:

    (p) 一直循环下去,直到走完整个链表,(p) 循环的时候,(q) 跟着 (p) 一起刷新
    (q)(next) 指针域始终指向 head->next;
    head->next; 始终指向上一个 (q)

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    void verge(linklist head) {
        linlist p, q;
        p = head->next;
        head->next = NULL;
    
        while (p) {
            q = p;
            p = p->next;
            q->next = head->next; // 通过 head->next 保留上一个 q 的状态
            head->next = q;
        }
    }
    

    8.6 拆分结点值为自然数的单链表,原链表保留值为偶数的结点,新链表存放值为奇数的结点(算法)

    设计一个算法,将一个结点值自然数的单链表拆分为两个单链表,原表中保留值为偶数的结点,而值为奇数的结点按它们在原表中的相对次序组成一个新的单链表

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    linklist sprit(linklist head) {
        linklist L, pre, p, r;
    
        L = r = (linklist) malloc(sizeof(linknode));
        r->next = NULL;
        pre = head;
        p = head->next;
    
        while (p) {
            if (p->data % 2 == 1) // 删除奇数值结点,并用 L 链表保存
            {
                pre->next = p->next;
                r->next = p;
                r = p; // 这样使得 r 变成了 r->next
                p = pre->next; // 这样使得 p 变成了 head->next->next
            } else // 保留偶数值结点
            {
                pre = p; // 书中的貌似多余操作
                p = p->next;
            }
        }
        r->next = NULL; // 置返回的奇数链表结束标记
        return L;
    }
    

    8.7 在有序单链表中删除值大于 x 而小于 y 的结点(算法)

    设计一个算法,对一个有序的单链表,删除所有值大于 x 而不大于 y 的结点

    typedef struct node {
        int data;
        struct node *next;
    } linknode;
    typedef linknode *linklist;
    
    void deletedata(linklist head, datatype x, datatype y) {
        linklist pre = head, p, q;
    
        p = head->next;
    
        // 找第 1 处大于 x 的结点位置
        while (p && p->data <= x) {
            pre = p;
            p = p->next;
        }
    
        // 找第 1 处小于 y 的位置
        while (p && p->data <= y) p = p->next;
    
        // 删除大于 x 而小于 y 的结点
        q = pre->next;
        pre->next = p; // 小于 x 的第一个结点指向大于 y 的第一个结点
        pre = q->next;
      	// 释放被删除结点所占用的空间
        while (pre != p) { // 此时 p 已经指向了大于 y 的第一个结点
            free(q);
            q = pre;
            pre = pre->next;
        }
    }
    

    九、错题集

    1. 在头结点的单链表中查找 (x) 应选择的程序体是:node *p = head; while (p && p->info != x) p = p->next; return p;

      1. 注:未找到时需要返回头结点 (head),而不是返回一个 (NULL)
    2. 用不带头结点的单链表存储队列时,其队头指针指向队头结点,其队尾指针指向队尾结点,则在进行删除操作时队头队尾指针都可能要修改

      1. 注:链式队列中只有一个结点是会出现该情况,插入时同理
    3. 若从键盘输入 (n) 个元素,则建立一个有序单向链表的时间复杂度为 (O(n^2))

      1. 注:第 (1) 个数:(0) 次查找;第 (2) 个数:(1) 次查找 (,cdots,)(n) 个数,(n-1) 次查找,总共 (n(n-1)/2)
  • 相关阅读:
    DataAnnotations
    使用BizTalk实现RosettaNet B2B So Easy
    biztalk rosettanet 自定义 pip code
    Debatching(Splitting) XML Message in Orchestration using DefaultPipeline
    Modifying namespace in XML document programmatically
    IIS各个版本中你需要知道的那些事儿
    关于IHttpModule的相关知识总结
    开发设计的一些思想总结
    《ASP.NET SignalR系列》第五课 在MVC中使用SignalR
    《ASP.NET SignalR系列》第四课 SignalR自托管(不用IIS)
  • 原文地址:https://www.cnblogs.com/nickchen121/p/13766832.html
Copyright © 2011-2022 走看看