线性表 - 数组与链表

感谢@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;
    }
}
posted @ 2024-01-23 09:25  白缺  阅读(6)  评论(0编辑  收藏  举报