代码随想录day03

链表理论基础

● 链表是一种通过指针串联在一起的线性结构,每一个节点有两个部分,数据域和指针域,最后一个节点指针域指向null

链表类型

● 单链表
● 双链表
○ 每个节点有两个指针域,一个指向下一个节点,一个是上一个节点
○ 可向前查询或向后查询
● 循环链表
○ 链表首尾相连
○ 可以解决约瑟夫环问题

存储方式

● 在内存中不连续分布,通过指针域连接在内存中的各个节点,散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理

定义

public class ListNode {
      int val;
      ListNode next;
      ListNode() {}
      ListNode(int val) { this.val = val; }
      ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 }

操作

● 删除
○ 前节点的next指向后节点,Java语言有自己的内存回收机制,不需要手动释放
● 添加
○ 前指我,我指后
● 都是O(1) 不影响其他节点,但注意,遍历查找的时间复杂度是O(n)
性能分析
● 数组插入删除O(n) 查询O(1)
● 链表插入删除O(1) 查询O(n)
● 数组定义的时候,长度固定,不能修改;链表可以动态增删,适合数据量不固定,增删频繁,查询少

203.移除链表元素

● 力扣题目链接
● 题意:删除链表中等于给定值 val 的所有节点。

思路

● 使用虚拟头节点或不使用,时间复杂度O(n) 空间复杂度O(1)
● 递归 时间复杂度O(n) 空间复杂度O(n)

代码

// 设置虚拟头节点
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode(-1, head); // 设置后减少讨论
        ListNode pre = dummy;
        while (pre.next != null) {
            if (pre.next.val == val) { //找到要删除的
                pre.next = pre.next.next; //删除
            } else {
                pre = pre.next; //向后移动
            }
        }
        return dummy.next; //头结点可能被删掉,返回dummy的next即可
    }
}
// 不设置虚拟头节点
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while (head != null && head.val == val) head = head.next; // 只要头结点要删除,就不断删除
        if (head == null || head.next == null) return head; // 删除完看是不是空,或只有一个节点,直接返回
        ListNode temp = head; // 这里temp.val不会是val,且后面有节点
        while (temp.next != null) {
            if (temp.next.val == val) temp.next = temp.next.next; // 删除
            else temp = temp.next;
        }
        return head; // 最后返回head即可
    }
}
// 递归
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) return null; // 终止条件
        head.next = removeElements(head.next, val); // 这个函数的作用是删除节点后返回头结点
        if (head.val == val) head = head.next; // 最后处理当前逻辑,如果是就删掉,向后移动
        return head; // 返回头结点
    }
}

707.设计链表

● 力扣题目链接
● 题意:
● 在链表类中实现这些功能:
○ get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
○ addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入 后,新节点将成为链表的第一个节点。
○ addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
○ addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
○ deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

思路

● 难度不大,一点一点分析,注意方法可以复用

代码

class MyLinkedList {

    private ListNode head;
    private int size;

    public MyLinkedList() {
        head = new ListNode(-1);
        size = 0;
    }
    
    public int get(int index) {
        if (index < 0 || index >= size) return -1;
        ListNode cur = head;
        while (index-- >= 0) {
            cur = cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }
    
    public void addAtIndex(int index, int val) {
        if (index > size) return;
        ListNode cur = head;
        while (index-- > 0) {
            cur = cur.next;
        }
        ListNode node = new ListNode(val);
        node.next = cur.next;
        cur.next = node;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        ListNode cur = head;
        while (index-- > 0) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        size--;
    }
}

class ListNode {
    int val;
    ListNode next;
    public ListNode(){};
    public ListNode(int val) {
        this.val = val;
    }
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

206.反转链表

● 力扣题目链接
● 题意:反转一个单链表。

思路

● 使用栈,时间复杂度O(n) 空间复杂度O(n)
● 递归逻辑,注意递归函数的作用,时间复杂度O(n) 空间复杂度O(n)
● 迭代逻辑,时间复杂度O(n) 空间复杂度O(1)

代码

// 使用栈
class Solution {
    public ListNode reverseList(ListNode head) {
        Deque<ListNode> stack = new ArrayDeque();
        while (head != null) {
            stack.addFirst(head); // 依次把节点入栈
            head = head.next;
        }
        if (stack.isEmpty()) return null; // 栈空,直接返回
        head = stack.removeFirst(); //先拿到头结点,因为要返回
        ListNode cur = head; // 使用cur标记
        while (!stack.isEmpty()) {
            cur.next = stack.removeFirst(); // 不断弹出节点
            cur = cur.next;
        }
        cur.next = null; // 这个很重要,不然会成环,必须把尾节点的next设为null
        return head;
    }
}
// 递归逻辑
class Solution {
    public ListNode reverseList(ListNode head) { // 1 -> 2 -> 3
        if (head == null || head.next == null) return head;
        ListNode newHead = reverseList(head.next); // 3 -> 2 <- 1
        head.next.next = head; // 3 -> 2 <=> 1
        head.next = null; // 3 -> 2 -> 1 -> null
        return newHead;
    }
}
// 迭代
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode pre = head;
        ListNode cur = pre.next;
        ListNode temp;
        while (cur != null) {
            temp = cur.next;
            cur.next = pre;
            if (pre == head) pre.next = null;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

35.搜索插入位置(补)

● 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
● 你可以假设数组中无重复元素。

思路

● 和正常的二分查找类似,分析一下应该插入的位置即可

代码

// 左闭右闭写法
class Solution {
    public int searchInsert(int[] nums, int target) {
        int l = 0, r = nums.length - 1, m;
        while (l <= r) {
            m = (l + r) >>> 1;
            if (nums[m] < target) l = m + 1;
            else if (nums[m] > target) r = m - 1;
            else return m;
        }
        return l; // 这个r + 1也可以
    }
}
// 左闭右开写法
class Solution {
    public int searchInsert(int[] nums, int target) {
        int l = 0, r = nums.length, m;
        while (l < r) {
            m = (l + r) >>> 1;
            if (nums[m] < target) l = m + 1;
            else if (nums[m] > target) r = m;
            else return m;
        }
        return l; // 这个r也可以
    }
}

你可能感兴趣的:(代码随想录,java)