博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据结构之链表
阅读量:3901 次
发布时间:2019-05-23

本文共 3458 字,大约阅读时间需要 11 分钟。

数据结构之链表

什么是链表

链表一种时间复杂度和空间复杂度为线性的数据结构,通过指针将一个个零散的内存块连接起来,链表的每个内存块称为结点。

链表的种类主要有三种,单链表,双链表,双向循环链表。

链表的优缺点

链表是一种链式结构,使得它对内存没有太大的要求。可以充分使用散乱的内存空间,插入和删除的时间复杂度比数组好。但是结构实现起来比较复杂,容易出错。而且不具备随机访问的特性,每次查询都要从头节点出发。需要付出额外的空间去保存下一个元素的指针。

单链表

单链表的每个节点有一个保存值的属性,还有一个指向下一个节点的指针。

从头节点出发可以完整的遍历整个链表。

#include 
#include
typedef struct ListNode{ struct ListNode *next; int val;} ListNode;

基本操作实现

初始化

void init(ListNode **head){    if (head == NULL)    {        return;    }    //为什么这里是ListNode ** head?    *head = (ListNode *)malloc(sizeof(ListNode));    (*head)->next = NULL;    (*head)->val = -1;}
为什么初始化的时候时List ** 类型
为什么这里是ListNode ** head?首先我们在main函数中定义的是LisNode * head,这个是指针如果我们函数传参的时候传递的是 head 也就是 ListNode * 类型的那我们的init函数中的head实际上是 main.head 的拷贝main.head.address != init.head.address (两个变量的地址不等,后续指向发生改变也不会同步)当我们分配空间之后 init.head 指向的是我们的申请分配的空间而 main.head 依然是 NULL当传入的是ListNode ** 时,init.head 指向的是main.head 的地址当*init.he ad 指向新分配的空间时,main.head 的指向也发生了改变一个原则 : 当你想改变一个变量的值时,就传递它的地址

增加元素

void insertNode(ListNode *head, int val, int index){    if (head == NULL)    {        return;    }    ListNode *h = head;    //定位到插入位置的前一个位置    while (index > 0 && h->next != NULL)    {        h = h->next;        index--;    }    ListNode *node = (ListNode *)malloc(sizeof(ListNode));    node->val = val;    //开始插入数据    node->next = h->next;    h->next = node;}

删除元素

void deleteNode(ListNode *head, int index){    if (head == NULL)    {        return;    }    ListNode *h = head;    while (index > 0 && h->next != NULL)    {        h = h->next;        index--;    }    //确保是删除最后一个元素    if (h->next != NULL)    {        ListNode *next = h->next;        h->next = h->next->next;        // 记得释放空间        free(next);    }}

双向链表

既然有了单链表,为什么还还要双链表呢?

单链表不管元素的在前面还是后面都需要从头节点出发,使用双链表我们可以根据元素的位置选择从头节点开始出发或者尾节点出发。
这里实现的只带头节点的双向链表。

结构定义

#include 
#include
typedef struct ListNode{ struct ListNode *last; struct ListNode *next; int val;} ListNode;

基本操作实现

链表初始化

void init(ListNode **head){    *head = (ListNode *)malloc(sizeof(ListNode));    (*head)->last = NULL;    (*head)->next = NULL;    (*head)->val = -1;}

插入操作

void insertNode(ListNode *head, int val, int index){    if (head == NULL || index < 0)    {        return;    }    ListNode *h = head;    //定位到要新元素的前一个位置    while (index > 0 && h->next != NULL)    {        h = h->next;        index--;    }    ListNode *node = (ListNode *)malloc(sizeof(ListNode));    node->val = val;    node->next = h->next;    // 如果当前位置之后还有元素,更新下个元素的指向    if(node->next != NULL){        h->next->last = node;    }    node->last = h;    h->next = node;}

删除操作

void deleteNode(ListNode *head, int index){    if (head == NULL || index < 0)    {        return;    }    ListNode *h = head;    //定位到要删除的元素的前一个位置    while (index > 0 && h->next != NULL)    {        h = h->next;        index--;    }    if (h->next != NULL)    {        //保存要删除的元素 释放空间        ListNode *next = h->next;        // 如果要函删除的元素之后还有元素 更新指向        if(h->next->next != NULL){            h->next->next->last = h;        }        h->next = h->next->next;        free(next);    }}

循环链表

尾指针指向的不是 NULL,而是指向我们的头节点。我们从头节点出发可以循环遍历到头节点。

在这里插入图片描述

如何快速写出正确的链表

链表的难度主要在元素发生变动时前后指针的更新。我们初学时不太熟悉,可以在纸上画出相应的图形去决定指针变换的顺序。同时需要多加联系,面试中经常遇到与链表相关的题目。

循环链表和带尾节点的链表这里就不实现了,大家可以在这个基础上自己去改造实现相应的数据结构。

尾插法和头插法

尾插法即将新元素插入在链表的末尾,头插即插入在头节点的下一个位置。

链表的时间复杂度一般为O(n),空间复杂度为O(n)

数据结构中相关的代码我已上传到gitlab:

此后的数据结构代码我都会上传在这个仓库中,需要的同学可以自己下载

感谢你能看懂这里,欢迎关注我的vx公众号:BitLegend,学习更多的知识!我们下期再见!

转载地址:http://mtden.baihongyu.com/

你可能感兴趣的文章
What really happens when you navigate to a URL
查看>>
偶遇with ties
查看>>
linux 编译指定库、头文件的路径问题
查看>>
使用gdb调试运行时的程序小技巧
查看>>
linux后端服务程序之信号处理
查看>>
Padding也要小心
查看>>
linux异步IO编程实例分析
查看>>
小组开发环境搭建: apache+ftp+cvs+samba
查看>>
静态库和动态库链接那些事--http://www.crazyshell.org/blog/?p=50
查看>>
一年成为Emacs高手(像神一样使用编辑器) .--http://blog.csdn.net/redguardtoo/article/details/7222501
查看>>
GNU make 指南
查看>>
配置 vim
查看>>
centos 安装emacs24
查看>>
【转】结构体中Char a[0]用法——柔性数组
查看>>
结构体最后定义一个char p[0];这样的成员有何意义(转)
查看>>
一步一学Linux与Windows 共享文件Samba (v0.2b)
查看>>
Linux 下忘记root密码怎么办
查看>>
Linux软件下载源码编程文章资料周立发--之调试
查看>>
GIT分支管理是一门艺术
查看>>
Cscope在emacs中的配置与使用
查看>>