最近在做关于链表的算法题,觉得有必要对链表的一些知识点做个梳理。当然都是通过看了各种文章组合起来的,感谢别人的分享。
1、链表与数组的差别
链表的特性是在中间任意位置添加删除元素的都非常的快,不需要移动其它的元素。通常链表每一个元素都要保存一个指向下一个元素的指针(单链表)。双链表的话每个元素即要保存到下一个元素的指针,还要保存一个上一个元素的指针。循环链表则把最后一个元素中保存下一个元素指针指向第一个元素。
数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,这个编号叫做下标,我们可以通过下标来区别这些元素。数组元素的个数有时也称之为数组的长度。
数组是将元素在内存中连续存放,由于每个元素占用内存相同,所以你可以通过下标迅速访问数组中任何元素。但是如果你要在数组中增加一个元素,你需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样的道理,如果你想删除一个元素,你同样需要移动大量元素去填掉被移动的元素。
链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。比如:上一个元素有个指针指到下一个元素,以此类推,直到最后一个元素。如果你要访问链表中一个元素,你需要从第一个元素开始,一直找到你需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了, 只要修改元素中的指针就可以了。
从上面的比较你可以看出,如果你的应用需要快速访问数据,很少或不插入和删除元素,你就应该用数组;相反, 如果你的应用需要经常插入和删除元素你就需要用链表数据结构了。
2、链表特点的代码分析
创建链表:
function ListNode(x){
this.val = x;
this.next = null;
}
链表长度分析(不能像数组那样直接使用 .length):
a、使用递归:
function length(head) {
return head ? 1 + length(head.next) : 0
}
b、使用循环:
function lengthV2(head) {
let len = 0
let node = head
while (node) {
len++
node = node.next
}
return len
}
单向链表的反转(让节点的下一节点指向自身,第一个节点指向null):
a、常规解法
function ReverseList(pHead)
{
// write code here
if(pHead == null){
return null;
}
var p = pHead.next;
var currHead = pHead;
pHead.next = null;
var pr;
while(p){
pr = p.next;
p.next = currHead;
currHead = p;
p = pr;
}
return currHead;
}
b、递归
function ReverseList(pHead)
{
// write code here
if(pHead==null){
return null;
}else if(pHead.next==null){
return pHead;
}
var currHead = ReverseList(pHead.next);
pHead.next.next = pHead;
pHead.next = null;
return currHead;
}
有关链表中元素的增加、删除、更改、查找,可看这篇文章:链表的处理