线性表 - 数组与链表
感谢@pdai的全栈知识体系,数据结构与算法是相通的,做的题目与原博主不同。 https://www.pdai.tech/md/algorithm/alg-basic-array.html
数组
数组是一种连续存储线性结构,元素类型相同,大小相等,数组是多维的,通过使用整型索引值来访问他们的元素,数组尺寸不能改变。
数组的优点:
- 存取速度快
数组的缺点:
- 事先必须知道数组的长度
- 插入删除元素很慢
- 空间通常是有限制的
- 需要大块连续的内存块
相关题目
简单难度
268. 丢失的数字 https://leetcode.cn/problems/missing-number/
class Solution {
public int missingNumber(int[] nums) {
// 人为添加0到n一共n+1个整数,将这些数和nums中所有数异或,则除了丢失的数,其余数字出现两次,得到结果
int xor = 0;
int n = nums.length;
for(int i = 0; i < n; i++){
xor ^= nums[i];
}
for(int i = 0; i <= n; i++){
xor ^= i;
}
return xor;
}
}
中等难度
16. 最接近的三数之和 https://leetcode.cn/problems/3sum-closest/
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length;
int best = Integer.MAX_VALUE;
// 枚举 a
for (int i = 0; i < n; ++i) {
// 保证和上一次枚举的元素不相等
if (i > 0 && nums[i] == nums[i-1]) {
continue;
}
// 使用双指针枚举 b 和 c
int j = i + 1, k = n - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
// 如果和为 target 直接返回答案
if (sum == target) {
return target;
}
// 根据差值的绝对值来更新答案
if (Math.abs(sum - target) < Math.abs(best - target)) {
best = sum;
}
if (sum > target) {
// 如果和大于 target,移动 c 对应的指针
int k0 = k - 1;
// 移动到下一个不相等的元素
while (j < k0 && nums[k0] == nums[k]) {
--k0;
}
k = k0;
} else {
// 如果和小于 target,移动 b 对应的指针
int j0 = j + 1;
// 移动到下一个不相等的元素
while (j0 < k && nums[j0] == nums[j]) {
++j0;
}
j = j0;
}
}
}
return best;
}
}
链表
n 个节点离散分配空间,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后续节点,首节点没有前驱,尾节点没有后继。确定一个链表我们只需要头指针,通过头指针就可以遍历整个链表。
优点
- 空间没有限制
- 插入删除元素很快
缺点
- 存取速度很慢
分类
- 单向链表:一个节点指向下一个节点
- 双向链表:一个节点有两个指针域,一个指向后继,一个指向前驱
- 循环链表:能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个节点指向第一个节点从而实现循环。
节点创建代码
public class Node {
// 数据域
public int data;
// 指针域,指向下一个节点
public Node next;
public Node() {
}
public Node(int data) {
this.data = data;
}
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
相关题目
链表是空节点,或者有一个值和指向下一个链表的指针,因此很多链表问题可以用递归来处理。
简单难度
234. 回文链表 https://leetcode.cn/problems/palindrome-linked-list/
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<>();
// 将链表的值复制到数组
ListNode currentNode = head;
while (currentNode != null) {
vals.add(currentNode.val);
currentNode = currentNode.next;
}
// 使用双指针判断是否回文
int left = 0;
int right = vals.size() - 1;
while (left < right) {
if (!vals.get(left).equals(vals.get(right))) {
return false;
}
left++;
right--;
}
return true;
}
}
中等难度
92. 反转链表 II https://leetcode.cn/problems/reverse-linked-list-ii/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 设置 dummyNode 是这一类问题的一般做法
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
ListNode cur = pre.next;
ListNode next;
// 使用头插法实现链表反转
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummyNode.next;
}
}
148. 排序链表 https://leetcode.cn/problems/sort-list/
class Solution {
public ListNode sortList(ListNode head) {
return sortList(head, null);
}
// 快慢指针找到中间节点,两边分别进行排序,然后再进行归并排序
public ListNode sortList(ListNode head, ListNode tail) {
if (head == null) {
return head;
}
if (head.next == tail) {
head.next = null;
return head;
}
ListNode slow = head, fast = head;
while (fast != tail) {
slow = slow.next;
fast = fast.next;
if (fast != tail) {
fast = fast.next;
}
}
ListNode mid = slow;
ListNode list1 = sortList(head, mid);
ListNode list2 = sortList(mid, tail);
ListNode sorted = merge(list1, list2);
return sorted;
}
public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 != null && temp2 != null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next;
} else {
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if (temp1 != null) {
temp.next = temp1;
} else if (temp2 != null) {
temp.next = temp2;
}
return dummyHead.next;
}
}