算法训练营三刷(Java) | 第一天~第四天

算法训练营三刷(Java) | 第一天~第四天

数组理论基础

在这里插入图片描述
在这里插入图片描述

第一天

LeetCode 704 二分查找

解题思路:双指针法
时间复杂度:O(log n)

左闭右闭写法
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) return mid;
            if (nums[mid] > target) right = mid - 1;
            if (nums[mid] < target) left = mid + 1;
        }
        return -1;
    }
}
左闭右开写法
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length;
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) return mid;
            if (nums[mid] > target) right = mid;
            if (nums[mid] < target) left = mid + 1;
        }
        return -1;
    }
}

LeetCode 27 移除元素

复杂思路:模拟,间隔移动

class Solution {
    public int removeElement(int[] nums, int val) {
        int num = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == val) num++;
        }
        num = nums.length - num;
        int start = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != val) continue;
            start = i;
            while (i < nums.length && nums[i] == val) {
                i++;
            }
            int interval = i - start;
            for (int j = start; j < nums.length - interval; j++) {
                nums[j] = nums[j + interval];
                nums[j + interval] = val;
            }
            i -= interval;
        }
        return num;
    }
}

注意点:每次替换完后i需要回退。

简单思路:双指针

冗杂错误代码示例:

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0, right = 1;
        while (left < right && right < nums.length) {
            while (right < nums.length && nums[right] == val) right++;
            while (left < right && nums[left] != val) left++;
            if (right < nums.length) {
                nums[left] = nums[right];
                nums[right] = val;
            }
        }
        return left;
    }
}

正确解法:

将左边视作右边不为val值元素的新数组。left表示的是新数组的下标。right找的是不为val的原数组中的元素。

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[right] != val) {
                nums[left] = nums[right];
                left++; right++;
            } else right++;
        }
        return left;
    }
}

第二天

LeetCode 977 有序数组的平方

class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) 
        {
            nums[i] = nums[i] * nums[i];
        }  
        Arrays.sort(nums);
        return nums;
    }
}

直接平方后重新排序即可

LeetCode 209 长度最小的子数组

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int len = nums.length;
        for (int i = 0; i < len; i++)
        {
            int num_in = nums[i];
            for (int j = i + 1; j < len; j++)
            {
                num_in += nums[j]; // 这里暗示的是连续子数组
                if (num_in >= target && j - i + 1 < len)
                {
                    len = j - i + 1;
                    break;
                }
            }
        }
        return len;
    }
}

LeetCode 59 螺旋矩阵II

解题思路:按照从左到右、从上到下、从右到左、从下到上的顺序用一个小于n的下标值计数。每一层分别填入对应的数目的元素。

class Solution {
    public int[][] generateMatrix(int n) {
        int top = 0, bottom = n-1;
        int left = 0, right = n-1;
        int count = 0;
        int[][] result = new int[n][n];
        while (count < n * n) {
            for (int i = left; i <= right; i++) {
                result[top][i] = count+1;
                count++;
            }
            for (int i = top+1; i <= bottom; i++) {
                result[i][right] = count+1;
                count++;
            }
            if (left < right && top < bottom) {
                for (int i = right-1; i >= left+1; i--) {
                    result[bottom][i] = count+1;
                    count++;
                }
                for (int i = bottom; i >= top+1; i--) {
                    result[i][left] = count+1;
                    count++;
                }
            }
            top++; left++; bottom--; right--;
        }
        return result;
    }
}

数组扩充题目

卡码网58 区间和

解题思路:双重循环会超时,这里用到前缀和的思想,即每一个位置处数组元素等于它前一个数组元素加上当前位置输入。每个元素实际含义可以看作是前面所有输入元素之和。
这样两个位置数组元素相减就可以用一重循环解决原先要用二重循环解决的区间求和问题。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n+1];
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt() + a[i-1];
        }
        while (sc.hasNext()) {
            int l = sc.nextInt(), r = sc.nextInt();
            int sum = a[r + 1] - a[l];
            System.out.println(sum);
        }
    }
}

卡码网44 开发商购买土地

解题思路:二维前缀和

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        int[][] area = new int[n+1][m+1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                area[i][j] = sc.nextInt()+area[i-1][j]+area[i][j-1]-area[i-1][j-1];
            }
        }
        int minDif = area[n][m];
        for (int i = 1; i < n; i++) {
            if (minDif > Math.abs(area[i][m] - area[n][m] + area[i][m]))
                minDif = Math.abs(area[i][m] - area[n][m] + area[i][m]);
        }
        for (int i = 1; i < m; i++) {
            if (minDif > Math.abs(area[n][i] - area[n][m] + area[n][i]))
                minDif = Math.abs(area[n][i] - area[n][m] + area[n][i]);
        }
        System.out.println(minDif);
    }
}

第三天

LeetCode 203 移除链表元素

解题思路:模拟+双指针+虚拟头节点

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode virtualHead = new ListNode(-1, head);
        ListNode cur = head;
        ListNode prev = virtualHead;
        while (cur != null) {
            while (cur != null && cur.val == val) cur = cur.next;
            prev.next = cur; 
            prev = cur;
            if (cur != null)
                cur = cur.next;
        }
        head = virtualHead.next;
        return head;
    }
}

LeetCode 707 设计链表

解题思路:
自己手动定义链表节点,并在类内定义虚拟头节点和链表长度。逐个实现即可。

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;
    }
}

class MyLinkedList {
    int size;
    ListNode head;

    public MyLinkedList() {
        head = new ListNode();
        size = 0;
    }
    
    public int get(int index) {
        if (index + 1 > size) return -1;
        ListNode cur = head;
        for (int i = 0; i <= index; i++) {
            cur = cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        ListNode newNode = new ListNode(val, head.next);
        head.next = newNode;
        size++;
    }
    
    public void addAtTail(int val) {
        ListNode cur = head;
        while (cur.next != null) cur = cur.next;
        cur.next = new ListNode(val);
        size++;
    }
    
    public void addAtIndex(int index, int val) {
        // 注意可以在末尾后面一个位置处插入元素
        if (index > size) return;
        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        ListNode newNode = new ListNode(val, cur.next);
        cur.next = newNode;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if (index + 1 > size) return;
        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        size--;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

LeetCode 206 反转链表

解题思路:
双指针法,注意最后要把原先头节点next指向空,否则会死循环

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) return head;
        ListNode prev = head;
        ListNode cur = head.next;
        while (cur != null) {
            ListNode tmp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = tmp;
        }
        head.next = null;
        return prev;
    }
}

第四天

LeetCode 24 两两交换链表中的节点

解题思路:三节点+虚拟头节点

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode virtualHead = new ListNode(0, head);
        ListNode prev = virtualHead;
        ListNode cur = head;
        ListNode next = head.next;
        while (cur != null && next != null) {
            ListNode tmp = next.next;
            next.next = cur;
            cur.next = tmp;
            prev.next = next;
            prev = cur;
            cur = tmp;
            if (tmp != null)
                next = tmp.next;
        }
        return virtualHead.next;
    }
}

LeetCode 19 删除链表的倒数第N个节点

解题思路:
计数反转+虚拟头节点+双指针

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode cur = head;
        ListNode virtualHead = new ListNode(0, head);
        int cnt = 1;
        while (cur != null) {
            if (cur.next != null) {
                cur = cur.next;
                cnt++;
            } else break;
        }
        cnt = cnt - n;
        cur = head;
        ListNode prev = virtualHead;
        while (cnt-- > 0) {
            prev = cur;
            cur = cur.next;
        }
        prev.next = cur.next;
        return virtualHead.next;
    }
}

面试题02.07.链表相交

解题思路:移到相同个数开始处,逐个比较

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int sizeA = 0;
        int sizeB = 0;
        while (curA != null) {
            sizeA++; curA = curA.next;
        }
        while (curB != null) {
            sizeB++; curB = curB.next;
        }
        int dis = Math.abs(sizeA-sizeB);
        if (sizeA > sizeB) {
            for (int i = 0; i < dis; i++) {
                headA = headA.next;
            }
        } else {
            for (int i = 0; i < dis; i++) {
                headB = headB.next;
            }
        }
        while (headA != headB) {
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }
}

LeetCode 142 环形链表II

解题思路:
通过快慢指针先找到相遇节点。如果没有说明没有环,直接返回null。
找到之后一个指针指向head,一个指向相遇节点,同步移动直到相遇就是入环节点。

注意开始时快慢指针不能放置在一处,否则直接退出循环。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) return null;
        ListNode fastIndex = head.next.next;
        ListNode slowIndex = head.next;
        while (fastIndex != null && slowIndex != null && slowIndex != fastIndex) {
            if (fastIndex.next == null) return null;
            fastIndex = fastIndex.next.next;
            slowIndex = slowIndex.next;
        }  
        if (fastIndex == null || slowIndex == null) return null;
        ListNode index = head;
        while (index != slowIndex) {
            index = index.next;
            slowIndex = slowIndex.next;
        }
        return index;
    }
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值