链表基础知识介绍
链表是一种基本的数据结构,用于组织和存储数据。数组和链表是常见的数据结构,它们在实现上有一些区别。数组由一组连续的内存空间组成,元素按顺序排列,并且可以通过索引值进行随机访问。但数组的大小固定,插入或删除元素需要进行大量的数据移动,效率较低。而链表则是由节点组成,每个节点包含一个数据元素和指向下一个节点的指针。链表的大小可以动态扩充或缩减,插入或删除元素只需修改指针,效率较高。但链表不支持随机访问,只能从头节点开始依次访问。相比数组,链表具有动态的大小,不需要预先分配内存空间,更容易进行插入和删除操作。
数组 vs. 链表
数组:
- 有序的数据集合。
- 存储在连续的内存块中。
- 随机访问速度快,时间复杂度为 O(1)。
- 插入和删除的平均时间复杂度为 O(n),因为需要移动元素。
链表:
- 由节点组成,每个节点包含数据和指向下一个节点的指针。
- 内存不连续,动态分配。
- 随机访问相对较慢,时间复杂度为 O(n)。
- 插入和删除的平均时间复杂度为 O(1),因为只需调整指针。
链表题目详解
1. 删除排序链表中的重复元素
LeetCode原题链接
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例 1:
输入: head = [1,1,2]
输出: [1,2]
示例 2:
输入: head = [1,1,2,3,3]
输出: [1,2,3]
题目解析
遍历链表,比较当前节点和下一个节点的值。如果相等,将当前节点的指针指向下一个节点的下一个节点,即跳过重复元素。
JavaScript 实例
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function(head) {
// if(head == null) return head
let cur = head
while(cur!==null && cur.next){
if(cur.val === cur.next.val){
cur.next = cur.next.next
}else{
cur = cur.next
}
}
return head
};
2. 合并两个有序链表
LeetCode原题链接
合并两个有序链表,返回合并后的有序链表。
示例 1:
输入: l1 = [1,2,4], l2 = [1,3,4]
输出: [1,1,2,3,4,4]
示例 2:
输入: l1 = [], l2 = []
输出: []
示例 3:
输入: l1 = [], l2 = [0]
输出: [0]
题目解析
创建一个新链表作为合并后的链表,使用两个指针分别遍历两个有序链表,比较节点值大小,逐个合并节点。
JavaScript 实例
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
const head = new ListNode();// val = 0, next = null
let cur = head;
while(list1 && list2){
if(list1.val <= list2.val){
cur.next = list1;
list1 = list1.next;
}else{
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
if(list1) cur.next = list1;
if(list2) cur.next = list2;
return head.next
};
思路详解1:
我们可以通过具体的链表题目来理解链表的应用。首先是题目「83. 删除排序链表中的重复元素」。给定一个已排序的链表,要求删除所有重复的元素,使每个元素只出现一次,并返回已排序的链表。我们可以使用双指针法来解决这个问题。定义一个指针cur指向头节点,然后遍历链表。如果当前节点的值等于下一个节点的值,说明有重复元素,将当前节点的next指针指向下一个节点的next,即跳过重复元素。如果不相等,将cur指针移动到下一个节点。最后返回头节点,即可得到删除重复元素后的链表。
思路详解2:
接下来是题目「21. 合并两个有序链表」。给定两个有序链表,要求将它们合并成一个有序链表并返回。我们可以使用归并排序的思想来解决这个问题。定义一个新的链表头节点head,并用cur指针指向它。然后比较两个链表的当前节点值,将较小的节点接在cur的next指针上,并将对应链表的指针移动到下一个节点。重复这个过程,直到遍历完其中一个链表。最后将剩余链表的节点接在cur的next指针上,即完成合并。返回head.next即可得到合并后的有序链表。
总结:
通过以上代码和解析,我们了解了链表的基本概念和常见操作方法。链表作为一种灵活的数据结构,在某些场景中具有较高的效率优势。对于涉及插入、删除等频繁操作的情况,链表的性能更好。希望这篇文章能够帮助你理解数组和链表的区别以及链表的应用。