数据结构学习源码_02

Class2链表的实现


本节课讲述数据结构顺序表的实现,源码及注释如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 定义链表的结点类型——数据区+地址区
typedef struct ListNode{
    int data;
    struct ListNode *next;
}ListNode;
// 定义链表类型,以便于为指定链表附加length属性,便于对链表进行管理
typedef struct LinkList{
    /*  这里不是定义一个ListNode型指针,而是定义了一个ListNode类型的元素(虚拟头节点)
        使用head的地址域来指向链表的第一个节点而不是让head作为第一个节点
        [head|val|&p1] -> [p1|val|&p2] -> [p2|val|p3] -> [p3|val|NULL] -> NULL
        由于插入和删除操作都要找到对应节点的前一个节点,因此对于上述结构,若对第0位进行操作无需移动,对第1位进行操作只需移动1位,对第2位进行操作只需移动2位,以此类推...
    */
    ListNode head;
    // 链表的元素个数
    int length;
}LinkList;

// 因为有两个结构(链表结构和链表节点结构),因此要对两个结构分别初始化
// 初始化链表节点,在插入新节点时调用
ListNode *init_listnode(int val){
    ListNode *p=(ListNode*)malloc(sizeof(ListNode));
    p->data = val;
    return p;
}

// 初始化链表
LinkList *init_linklist(){
    LinkList *l=(LinkList*)malloc(sizeof(LinkList));
    // 链表的第一个节点为空
    l->head.next = NULL;
    l->length=0;
    return l;
}

// 释放节点空间,在释放链表空间时调用
void clear_listnode(ListNode *node){
    // 若该节点为空,则直接返回
    if (node == NULL) return ;
    // 释放该节点
    free(node);
    return ;
}

// 释放链表空间
void clear_linklist(LinkList *l){
    // 若该链表为空,释放该链表
    if (l == NULL) return;
    // 否则,从头节点开始遍历每一个节点并释放节点空间
    ListNode *p = l->head.next, *q;
    while (p){
    	// !!!先用q保存循环因子p的下一个节点的地址,防止后续节点丢失
        q = p->next;
        //!!!再对节点进行释放
        clear_listnode(p);
        //!!!最后一步通过交换地址实现遍历
        p = q;
    }
    free(l);
    return;
}

// 指定位置插入节点
int insert(LinkList *l, int ind, int val){
    // 若链表为空,插入失败
    if (l == NULL) return 0;
    // 若插入位置在第一个元素之前或在最后一个元素之后两位,插入失败
    if (ind < 0 || ind > l->length) return 0;
    // 取得head的地址以访问链表并初始化要插入的新节点
    // node用来指向要插入的节点
    ListNode *p = &(l->head), *node = init_listnode(val);
    while(ind--){
    	// 用p找到待插入位置的前一个节点
        p=p->next;
    }
    // !!!第一步先设置要插入的节点的指针区以保存其后续节点的地址
    node->next = p->next;
    // !!!第二步再设置前一个节点的指针区指向要插入的节点的地址
    p->next = node;
    // 链表长度加一
    l->length++;
    return 1;
}

int erase(LinkList *l, int ind){
    if (l == NULL) return 0;
    if (ind < 0 || ind >= l->length) return 0;
    // 用指针p找到待删除节点的前一个节点
    ListNode *p = &(l->head), *q;
    while (ind--){
        p = p->next;
    }
    // 先用q保存待删除节点的后一个节点的地址
    q = p->next->next;
    // 直接释放p->next节点
    clear_listnode(p->next);
    //将待删除节点的前一个节点与后一个节点相连
    p->next = q;
    // 链表长度减一
    l->length--;
    return 1;
}

// 输出整个链表
void output(LinkList *l){
    printf("LinkList(%d): ", l->length);
    // p非空(p!=NULL)也可以直接写成p
    for (ListNode *p = l->head.next; p!=NULL; p = p->next){
        printf("%d -> ", p->data);
    }
    printf("NULL\n");
    return ;
}

#define MAX_OP 30
int main(){
    srand(time(0));
    LinkList *l = init_linklist();
    for(int i = 0; i < MAX_OP; i++){
        int op = rand() % 3;
        // 测试用位置在0到l->length之间
        int ind = rand() % (l->length + 1);
        int val = rand() % 100;
        switch (op)
        {
            // 当哦op为0或1时均执行插入操作,实现66%的概率测试插入操作
            case 0:
            case 1: {
                printf("insert %d at %d to LinkList = %d\n",
                 val, ind, insert(l, ind, val));
            } break;
            case 2: {
                printf("erase item at %d from LinkList = %d\n",
                ind, erase(l, ind));
            } break;
        }
        output(l);
        printf("\n");
    }
    clear_linklist(l);
    return 0;
}

重要执行函数实现过程:

在这里插入图片描述如图要把指针node所指向的④号节点插入到②号节点的位置

在这里插入图片描述先遍历整个链表,用指针p找到②号节点的前一个节点

在这里插入图片描述将④号节点的地址区(下一个节点的地址)指向p->next,也就是②号节点的地址。

在这里插入图片描述将指针p所指节点的地址区指向④号节点,也就是p->next=node

在这里插入图片描述大功告成!一定要注意插入操作的先后顺序,若先将p->next指向node则会丢失后面的所有节点,造成内存泄漏!!!另外对于单向循环列表,为方便进行插入和删除节点的操作,一般把head所指向的节点看作尾节点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构》(C语言版) 算法源码及运行演示系统使用说明 一、启动演示系统 双击演示系统应用程序文件“DS_VC_ALGO.EXE”启动演示系统,出现图1所示界面。 图1 《数据结构》(C语言版)算法源码及运行演示系统主界面 二、演示系统使用步骤 除了个别算法之外,演示系统给出了《数据结构》(C语言版)书中算法对应的程序代码(CPP文件)和测试运行程序(VC++6.0的EXE文件)。通过本系统,可以显示算法的源代码以及运行结果。具体操作步骤如下: 1.选择相应章 单击演示系统界面右侧章选择按钮。 例如,要选择第6章,则单击“第6章”选择按钮。 当相应章被选择后,窗口的右侧部分将列出本章的算法选择按钮。 例如,选择第6章后,窗口的右侧部分将显示第6章中的算法6.1-6.13和6.15的选择按钮。由于书中的算法6.14和6.16只是示意性算法,故未给出源码,其按钮上的文字为灰色,处于“无效”状态。 2.选择相应章中的算法 单击窗口右侧部分所列举的本章某个算法选择按钮,被选择的算法的源码将在窗口左侧空白区域中显示。对于较长的源码,单击显示区域后,可用键盘的光标键和翻页键浏览源码。 例如,选择了第6章中的算法6.5后界面如图2所示: 图2 选择算法6.5 3.运行测试程序 单击窗口上部的“运行”按钮,将弹出运行窗口,运行所选算法的测试程序。若运行按钮为灰色,表示该算法无单独测试程序。 例如,算法6.5的测试运行窗口如图3所示: 图3 测试运行窗口 测试运行说明: 测试运行窗口显示程序的执行过程及结果。若在显示过程中出现运行窗口无法正常演示的情况,只需调节运行窗口大小即可正常显示(调节最小化按钮或窗口最大化/还原按钮“ ”)。 三、退出演示系统 使用完毕后,单击窗口右上角关闭按钮“ ”退出演示系统。 四、测试程序示例 在《数据结构》的课程教学中,各抽象数据类型的设计与实现是重要的学习和实践环节。为此,本系统只给出了各算法源码的测试程序的可执行文件。在此,给出算法6.5的测试程序示例,以供参考。 算法6.5是中序遍历线索二叉树的非递归算法,要对其源码进行测试,可首先调用算法6.6及6.7建立中序线索二叉树。以下是测试程序的源码,相关类型和辅助函数定义在文件include06.h和include06.cpp中,此略。 // test0605.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "include06.h" // 相关类型和辅助函数的定义 BiThrTree pre; // 线索二叉树遍历辅助变量 #include "algo0607.cpp" // 算法6.7源码 #include "algo0606.cpp" // 算法6.6源码 #include "algo0605.cpp" // 算法6.5源码 int main(int argc, char* argv[]) { char gl_str[64]; BiThrTree T; BiThrTree Thrt; printf("*******************************************\n"); printf("* 《数据结构》(C语言版)严蔚敏,吴伟民 *\n"); printf("* 算法6.5, 6.6 & 6.7 *\n"); printf("*******************************************\n"); srand((unsigned)time(NULL)); // 随机函数初始化 T=NULL; // 空二叉树T for (int pass=0; pass<5; pass++) { // 测试运行5次,第一次为空树 outBiThrTree(T,gl_str); // 以类广义表的形式输出二叉树T到gl_str printf("T = %s\n", gl_str); // 显示 pre = NULL; Status r = InOrderThreading(Thrt, T); // 算法6.6,6.7,中序线索化 printf("InOrderThreading(Thrt, T) : %s\n", (r) ? "OK" : "ERROR"); initVisitStr(); // 将visitStr清为空串 InOrderTraverse_Thr(Thrt, v

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值