2.链表

C语言数据结构基础——链表

链表是线性表的一种,每个节点里存储了数据域和指针域,其中指针域中存储指向下一个节点的指针。相比顺序表,链表在插入和删除操作上更方便。链表有很多不同类型:单向链表、双向链表、循环链表等。

链表定义

我们把链表想象成火车,火车头便是链表的表头,每节车厢就是链表的元素,车厢里载的人和物就是元素的数据域,连接车厢的部件就是元素的指针。

由此,我们可以得出链表的一个特点:元素之间前后依赖,串联而成。相比于数据域成片的顺序表链表的元素不能随机访问。链表中元素前后连接是单一的,即元素前面和后面不会出现多个元素相连的情况。

链表基本操作的实现
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Node {
    int data;
    struct Node *next;
} Node;
typedef struct List {
    Node *head;  // 头指针地址
    int length;  // 链表长度
} List;
/*
* 链表初始化操作
* @return 链表指针
* */
List *init_list() {
    List *l = (List *)malloc(sizeof(List));
    l->head = NULL;
    l->length = 0;
    return l;
}
/*
* 插入操作,时间复杂度O(n)
* @param l 待操作链表
* @param ind 待插入坐标
* @param val 待插入元素值
* */
int insert(List *l, int ind, int val) {
    if(l == NULL) return 0;                     // 链表未初始化
    if(ind < 0 || ind > l->length) return 0;    // 判断坐标是否合法
    Node *node = (Node *)malloc(sizeof(Node));  // 新建一个链表结点
    node->data = val;
    node->next = NULL;
    if(ind == 0) {                              // 坐标为0,需要更新头指针
        node->next = l->head;
        l->head = node;
    }else {
        Node *p = l->head;                      // 新建一个临时指针指向链表头
        while(--ind) p = p->next;               // 移动指针到ind前一个位置
        node->next = p->next;                   // 将当前结点的指针指向原本ind位置
        p->next = node;                         // ind的前一个结点指向当前新结点
    }
    l->length++;                                // 更新链表长度
    return 1;
}
/*
* 删除操作 时间复杂度O(n)
* @param l待操作链表
* @param ind 待删除元素的坐标
* */
int delete(List *l, int ind) {
    if(l == NULL) return 0;                     // 链表未初始化
    if(ind < 0 || ind >= l->length) return 0;    // 判断坐标是否合法
    Node *delete_node;
    if(ind == 0) {                              // 坐标为0,需要更新头指针
        delete_node = l->head;
        l->head = l->head->next;
    }else {
        Node *p = l->head;                      // 新建一个临时指针指向链表头
        while(--ind) p = p->next;               // 移动指针到ind前一个位置
        delete_node = p->next;
        p->next = delete_node->next;            // ind前一个位置的指针指向ind后一个位置
    }
    free(delete_node);                                 // 释放指定结点
    l->length--;                                // 更新链表长度
    return 1;
}
/**
* 反转操作 时间复杂度 O(n)
* @param l待操作链表
* */
void reverse(List *l) {
    if(l == NULL || l->length < 2) return;      // 链表未初始化或者,长度在1以内不需要反转
    Node *p = l->head;                          // 定义一个结点指向链表头
    l->head = NULL;                             // 将头结点都链表上拆下来
    while(p) {                                  // 头结点指向当前结点的后两个结点
        Node *temp = p->next;                   // 定义一个临时结点指向当前结点的下一个
        p->next = l->head;                      // 将当前结点从链表上取下来
        l->head = p;                            // 当前结点更新为头结点
        p = temp;                               // 移动当前指向
    }
    return;
}
/*
* 清理链表 时间复杂度 O(n)
* @param l待操作链表
* **/
void clear(List *l) {
    if(l == NULL) return;                       // 判断链表是否已经初始化
    Node *p = l->head;                          // 定义一个结点指向链表头
    while(p) {
        Node *temp = p->next;                   // 定义临时结点指向后一个结点
        free(p);                                // 释放当前结点
        p = temp;                               // 更新当前链表
    }
    free(l);
}
/*
* 查询链表 时间复杂度 O(n)
* @param l待操作链表
* @param val 待查找的值
* */
int search(List *l, int val) {
    if(l == NULL) return -1;                    // 判断链表是否已经初始化
    Node *p = l->head;                          // 定义一个结点指向链表头
    int i = 0;
    while(p) {
        if(p->data == val) return i;
        p = p->next;
        i++;
    }
    return -1;
}
/*
* 遍历链表 时间复杂度 O(n)
* @param l待操作链表
* **/
void output(List *l) {
    if(l == NULL) return;                       // 判断链表是否已经初始化
    Node *p = l->head;                          // 定义一个结点指向链表头
    printf("List:[");
    while(p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("]\n");
}
int main() {
    srand(time(0));
    #define MAX_OP 20
    List *l = init_list();
    for(int i = 0; i < MAX_OP; i++) {
        int op = rand() % 4;
        int val = rand() % 100;
        int ind = rand() % (l->length + 3);
        if(op) {
            int res = insert(l, ind, val);
            printf("insert %d at %d to List = %d\n", val, ind, res);
        }else {
            int res = delete(l, ind);
            printf("erase a item at %d from List = %d\n", ind, res);
        }
        output(l), printf("\n");
    }
    reverse(l);
    output(l);
    clear(l);
    #undef MAX_OP
    return 0;
}
循环链表的基本操作

循环链表,相比单链表,循环链表不同的是它将最后一个结点的指针指向了头结点,这样的结构使得链表更加灵活方便。循环链表里没有空指针,所以在判断结束条件时,不再是判断指针是否为空,而是判断指针是否等于某固定指针。另外,在单链表里,一个节点只能访问到它后面的结点,而在循环链表里它可以访问到所有的结点。

/*************************************************************************
  > File Name: 2.linklist_loop.c
  > Author: 陈杰
  > Mail: 15193162746@163.com
  > Created Time: 2021年03月29日 星期一 11时31分10秒
  > 循环链表的基本操作
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
/*链表节点定义*/
typedef struct Node {
    int data;
    struct Node *next;
} Node;
/*链表定义*/
typedef struct List{
    Node *head;
    int length;
} List;
/*
* 初始化链表
* */
List *init() {
    List *l = (List *)malloc(sizeof(List));     // 动态申请指针域
    l->head = NULL;                             // 默认链表指针指向空
    l->length = 0;
}
/*
* 插入元素 时间复杂度O(n)
* @param l: 要操作的链表指针
* @param ind: 要插入的下标点
* @param val: 要插入元素的值
* */
int insert(List *l, int ind, int val) {
    if(l == NULL) return 0;                     // 判断链表是否已经初始化
    if(ind < 0 || ind > l->length) return 0;    // 判断要插入的下标是否合法
    Node *node = (Node *)malloc(sizeof(Node));  // 动态申请一个节点,并初始化
    node->data = val;
    node->next = NULL;
    /*判断链表是否为空*/
    if(l->head == NULL) {
        l->head = node;
        l->head->next = node;
    }else {
        Node *p = l->head;
        int index = ind;
        while(index--) p = p->next;             // 将链表移动到指定位置的前一个下标
        node->next = p->next;
        p->next = node;
        if(ind == l->length) l->head = node;    // 若插入坐标为最后一位,更新头节点
    }
    l->length++;
    return 1;
}
/*
* 删除元素 时间复杂度为O(n)
* @param l: 要操作的链表指针
* @param ind: 要删除的下标点
* **/
int delete(List *l, int ind) {
    if(l == NULL) return 0;                     // 判断链表是否已经初始化
    if(ind < 0 || ind >= l->length) return 0;   // 判断要插入的下标是否合法
    Node *p = l->head, *delete_node;
    if(l->length == 1) {
        delete_node = l->head;
        l->head = NULL;
    }else {
        while(ind--) p = p->next;               // 将链表移动到指定位置的前一个下标
        delete_node = p->next;                  // 记录要删除的节点地址
        p->next = delete_node->next;
        if(delete_node == l->head) l->head = delete_node->next;
    }
    free(delete_node);
    l->length--;
    return 1;
}
/*
* 反转链表 时间复杂度O(n)
* @param l: 要操作的链表指针
* **/
void reverse(List *l) {
    if(l == NULL || l->length < 2) return;      // 链表为空或者长度小于2什么都不用做
    Node *p,*head = l->head;                    // 定义一个节点指向链表尾部
    Node *begin = p = head->next;
    l->head = NULL;                             // 把头节点从循环链表上拆下来
    Node *temp = NULL;
    while(p != head){                           // 从头到位循环到尾节点前一个
        temp = p->next;
        p->next = l->head;
        l->head = p;
        p = temp;
    }
    // 链表变成一个单链表及一个节点head。
    head->next = l->head;                       // temp等于前头结点,当前的尾节点
    begin->next = head;
    l->head = begin;
}
/*
* 清理链表 时间复杂度O(n)
* @param l: 要操作的链表指针
* **/
void clear(List *l) {
    if(l == NULL) return;                       // 判断链表是否已经初始化
    if(l->head == NULL) {
        free(l);
        return;
    }
    Node *p,*head = l->head;                    // 定义一个节点指向链表头
    p = head->next;
    while(p != head){
        Node *temp = p->next;
        free(p);
        p = temp;
    }
    free(head);
    free(l);
}
/*
* 打印链表 时间复杂度O(n)
* @param l: 要操作的链表指针
* **/
void output(List *l) {
    if(l == NULL || l->head == NULL) return;    // 判断链表是否已经初始化,且链表不能为空
    Node *p,*head = l->head;                    // 定义一个节点指向链表头
    p = head->next;
    printf("List:[");
    while(p != head){
        printf("%d ", p->data);
        p = p->next;
    }
    printf("%d]\n", head->data);
}
int main() {
    srand(time(0));
    #define MAX_OP 20
    List *l = init();
    for(int i = 0; i < MAX_OP; i++) {
        int op = rand() % 4;
        int val = rand() % 100;
        int ind = rand() % (l->length + 1);
        if(op) {
            int res = insert(l, ind, val);
            printf("insert %d at %d to List = %d\n", val, ind, res);
        }else {
            int res = delete(l, ind);
            printf("erase a item at %d from List = %d\n", ind, res);
        }
        output(l), printf("\n");
    }
    reverse(l);
    output(l);
    clear(l);
    #undef MAX_OP
    return 0;
}
双链表的基本操作

双向链表也叫双链表。单链表里的指针域只记录了结点的下一个结点,也就是后继结点,而双向链表的指针域还记录了结点的上一个结点,也就是前驱结点。有了这样的结构,我们可以从头结点遍历到尾结点,也可以从尾结点遍历到头结点了。

双向链表操作与单项链表相似,唯一需要注意的就是操作时头尾指针均需要变化。

/*************************************************************************
  > File Name: 2.linklist_binary.c
  > Author: 陈杰
  > Mail: 15193162746@163.com
  > Created Time: 2021年03月29日 星期一 09时15分34秒
  > 链表的基本操作
************************************************************************/
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Node {
    int data;
    struct Node *next;
    struct Node *prov;
} Node;
typedef struct List {
    Node *head;  // 头指针地址
    int length;  // 链表长度
} List;
/*
* 链表初始化操作
* @return 链表指针
* */
List *init_list() {
    List *l = (List *)malloc(sizeof(List));
    l->head = NULL;
    l->length = 0;
    return l;
}
/*
* 插入操作,时间复杂度O(n)
* @param l 待操作链表
* @param ind 待插入坐标
* @param val 待插入元素值
* */
int insert(List *l, int ind, int val) {
    if(l == NULL) return 0;
    if(ind < 0 || ind > l->length) return 0;
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = val;
    node->next = NULL;
    node->prov = NULL;                          // 初始化prov
    if(ind == 0) {
        node->next = l->head;
        if(l->head) l->head->prov = node;       // 判断头结点是否为空,否则更新头结点的prov
        l->head = node;
    }else {
        Node *p = l->head;
        while(--ind) p = p->next;
        node->prov = p;
        node->next = p->next;
        if(p->next) p->next->prov = node;       // 判断下标后的点是否为空,否则更新后面点的prov值
        p->next = node;
    }
    l->length++;
    return 1;
}
/*
* 删除操作 时间复杂度O(n)
* @param l待操作链表
* @param ind 待删除元素的坐标
* */
int delete(List *l, int ind) {
    if(l == NULL) return 0;
    if(ind < 0 || ind >= l->length) return 0;
    Node *delete_node;
    if(ind == 0) {
        delete_node = l->head;
        l->head = l->head->next;
        if(l->head) l->head->prov = NULL;       // 判断头结点是否为空,否则更新头结点的prov
    }else {
        Node *p = l->head;
        while(--ind) p = p->next;
        delete_node = p->next;
        p->next = delete_node->next;
        if(delete_node->next) delete_node->next->prov = p;  // 判断删除节点后面是否有点,有的话更改其prov
    }
    free(delete_node);
    l->length--;
    return 1;
}
/**
* 反向打印 时间复杂度 O(2n)
* @param l待操作链表
* */
void reverse(List *l) {
    if(l == NULL) return;
    Node *p = l->head;
    while(p && p->next)  p = p->next;           // 将指针移至最后一个节点
    printf("reList:[");
    while(p) {
        printf("%d ", p->data);
        p = p->prov;
    }
    printf("]\n");
}
/*
* 清理链表 时间复杂度 O(n)
* @param l待操作链表
* **/
void clear(List *l) {
    if(l == NULL) return;
    Node *p = l->head;
    while(p) {
        Node *temp = p->next;
        free(p);
        p = temp;
    }
    free(l);
}
/*
* 查询链表 时间复杂度 O(n)
* @param l待操作链表
* @param val 待查找的值
* */
int search(List *l, int val) {
    if(l == NULL) return -1;
    Node *p = l->head;
    int i = 0;
    while(p) {
        if(p->data == val) return i;
        p = p->next;
        i++;
    }
    return -1;
}
/*
* 遍历链表 时间复杂度 O(n)
* @param l待操作链表
* **/
void output(List *l) {
    if(l == NULL) return; 
    Node *p = l->head;
    printf("List:[");
    while(p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("]\n");
}
int main() {
    srand(time(0));
    #define MAX_OP 20
    List *l = init_list();
    for(int i = 0; i < MAX_OP; i++) {
        int op = rand() % 4;
        int val = rand() % 100;
        int ind = rand() % (l->length + 1);
        if(op) {
            int res = insert(l, ind, val);
            printf("insert %d at %d to List = %d\n", val, ind, res);
        }else {
            int res = delete(l, ind);
            printf("erase a item at %d from List = %d\n", ind, res);
        }
        output(l), printf("\n");
    }
    reverse(l);
    output(l);
    clear(l);
    #undef MAX_OP
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值