数组—顺序存储
数组作为一个顺序储存方式的数据结构,可是有大作为的,它的灵活使用为我们的程序设计带来了大量的便利;
但是,但是,数组最大的缺点就是我们的插入和删除时需要移动大量的元素,所以呢,大量的消耗时间,以及冗余度难以接受了。
以C语言数组插入一个元素为例,当我们需要在一个数组{1,2,3,4}
的第1个元素后的位置插入一个’A’时,我们需要做的有:
- 将第1个元素后的整体元素后移,形成新的数组
{1,2,2,3,4}
- 再将第2个元素位置的元素替换为我们所需要的元素’A’
- 最终形成我们的预期,这需要很多的操作喔。
上图可以看出,使用数组都有这两大缺点:
- 插入删除操作所需要移动的元素很多,浪费算力。
- 必须为数组开足够的空间,否则有溢出风险。
链表—链式存储
由于数组的这些缺点,自然而然的就产生链表的思想了。
链表通过不连续的储存方式,自适应内存大小,以及指针的灵活使用,巧妙的简化了上述的内容。
链表的基本思维是,利用结构体的设置,额外开辟出一份内存空间去作指针,它总是指向下一个结点,一个个结点通过NEXT指针相互串联,就形成了链表。
其中DATA为自定义的数据类型,NEXT为指向下一个链表结点的指针,通过访问NEXT,可以引导我们去访问链表的下一个结点。
对于一连串的结点而言,就形成了链表如下图:
上文所说的插入删除操作只需要修改指针所指向的区域就可以了,不需要进行大量的数据移动操作。如下图:
相比起数组,链表解决了数组不方便移动,插入,删除元素的弊端,但相应的,链表付出了更加大的内存牺牲换来的这些功能的实现。
链表概述
包含单链表,双链表,循环单链表,实际应用中的功能不同,但实现方式都差不多。
链表中重要思想:快慢指针问题
例如:
难度简单
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
注意:本题相对原题稍作改动
示例:
输入:1->2->3->4->5 和 k=2
输出:4
说明:给定的k保证是有效的
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int kthToLast(ListNode head, int k) {
//双指针--->快慢指针来反转链表
ListNode pre =head;
ListNode cur=null;
while(pre!=null){
ListNode temp=pre.next;
pre.next=cur;
cur=pre;
pre=temp;
}
int i=0;
int res=0;
ListNode list=cur;
while(list!=null){
if(i==(k-1)){
res=list.val;
}
list=list.next;
i++;
}
return res;
}
}
上图中的快慢指针是这样实现的:
(1)
(2)
(3)