链表,最省内存的数据结构

链表,是数据结构比较基础的一种数据结构。它具有节省内存,需要多少就创建多少的特性。


1.什么是链表

链表是一种比较节省内存的数据结构,它的每个元素是一个节点node每个node包含了存储值value和下个节点指针next,指向下一个node。在数组中,我们需要先为数据分配一定大小的空间,比如int a[10]。但实际上,我们有可能用不到那么多的空间,比如只使用了a[0]和a[1]。这样的话,后面的空间就被浪费了。在链表中,就不存在浪费空间的情况,因为链表的原则就是:用多少就创建多少

2.创建链表

我们先来看看如何创建链表,创建元素有两种方法,分别是头插法尾插法。头插法,每次新插入的元素都会在链表。尾插法,**每次新插入的元素都会在链表结尾。**以[90,85,100,99]四个元素为例,使用头插法存储的话,逻辑顺序会反过来,变成99,100,85,90。而尾插法则依旧是90,85,100,99。如何存储这四个元素呢?

image

为了操作方便,在链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以存储数据标题、表长等信息,也可以不存储任何信息,其指针域存储第一个结点的首地址。加入头结点的目的是为了代码的简洁。

//头插法
Node head = new Node();//创建头结点
head.next = NULL; //一开始没有数据,head节点的下一节点指针指向空。
//假设输入数据[90,85,100,99]存在数组a中
for(int i=0;i<n;i++){
    Node new_node = new Node();
    new_node.value = a[i];
    new_node.next = head.next;//把head的next赋值给new_node
    head.next = new_node;
}
//尾插法
Node head = new Node();//创建头结点
head.next = NULL; //一开始没有数据,head节点的下一节点指针指向空。
cur_node = head; //当前遍历的指针,指向头结点
//假设输入数据[90,85,100,99]存在数组a中
for(int i=0;i<n;i++){
    Node new_node = new Node();
    new_node.value = a[i];
    cur_node.next = new_node;//当前节点的下一节点指针指向新创建的节点
    cur_node = new_node;//当前节点指向新的节点
}

无论是头插法还是尾插法,新插入一个节点,只需要新建一个节点,修改他的value和next就可以,其时间复杂度为O(1)。

3.查找元素

怎么在链表中查找数据呢?链表的存储在内存当中是不连续的,因为只记录了头结点的位置,所以当一个链表创建完成后,无论我们要对链表做什么操作,只能够从链表的head节点开始访问链表。可以得知,每次查找链表中数据,最坏情况下需要遍历整个链表才能找到,因此链表中查找数据的时间复杂度为O(n)。其代码如下:

Node find_node(Node head,int value){
    Node cur_node = head.next;
    while(cur_node != null){
        if(cur_node.value == value)
            return cur_node;
    }
    return null;
}

4.删除元素

在链表中删除节点x,我们需要找到的是x的前一个节点,将其前一个节点的指针指向x的后一个节点。如图所示,如果要删除node3,需要找到node2,将node2指向node4。

image

然而,单链表中并没有存储前一个节点的指针,所以,我们只能够从头开始遍历,找到x的前一个节点。代码如下:

void delete_node(Node head,Node deleteNode){
    Node cur_node = head;
    Node next_node = head.next;
    while(next_node!=deleteNode){//找到delete_node及其前驱节点
        cur_node = cur_node.next;
        next_node = next_node.next;
    }
    cur_node.next = next_node.next; //删除next_node节点
    free(next_node);//释放next_node节点的内存
}

由于删除节点的需要先遍历链表,因此它的时间复杂度也是O(n)。然而,存在一种在O(1)时间内可以删除节点的方法。只需把删除结点i的下一个结点j的内容复制到i上,然后把i的指针指向j的next,然后再删除j结点。等价于删除了i结点。整个过程无需遍历整个链表,时间复杂度是O(1)级别。如图所示,要删除node3,先把node4的值复制到node3,再删除node4,等价于删除了node3。不过这种方法虽然速度快,但是在工程上实践一般不这样做,因为可能产生其他的bug。

image

5.总结

**时间复杂度:访问元素O(n),查找元素O(n),插入元素O(1),删除元素O(n)(可以优化O(1))
优点
:**节省内存,用多少就取多少。
**缺点:**失去了顺序存储结构随机存取的特性,访问效率低下。


公众号:萌凯的程序人生
一枚IT研究生,致力于分享自己的学习经历和总结!
喜欢的话请点赞关注,谢谢您!

image

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值