力扣刷题笔记-1

力扣刷题笔记

数组&链表

前缀和数组

303. 区域和检索 - 数组不可变

给定一个整数数组 nums,处理以下类型的多个查询:

计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= right
实现 NumArray 类:

NumArray(int[] nums) 使用数组 nums 初始化对象
int sumRange(int i, int j) 返回数组 nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] + nums[left + 1] + … + nums[right] )

class NumArray {

    private int[] preNums;

    public NumArray(int[] nums) {
        preNums = new int[nums.length +1 ];

        for(int i = 1 ; i < preNums.length;i++){
            preNums[i] = preNums[i - 1] + nums[i-1];
        }
    }
    
    public int sumRange(int left, int right) {
            return preNums[right + 1] - preNums[left];
    }
}

核⼼思路是我们 new ⼀个新的数组 preSum 出来,preSum[i] 记录 nums[0…i-1] 的累加和,(通俗的说就是先把每个前i项和求出来放在一个新的数组中,当在调用时不用每次都去用for循环遍历,让时间复杂度变为O(1)了)

力扣刷题笔记-1_第1张图片

看这个 preSum 数组,如果我想求索引区间 [1, 4] 内的所有元素之和,就可以通过 preSum[5] - preSum[1] 得出。

304. 二维区域和检索 - 矩阵不可变

给定一个二维矩阵 matrix,以下类型的多个请求:

计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。
实现 NumMatrix 类:

NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
int sumRegion(int row1, int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2) 所描述的子矩阵的元素 总和 。

力扣刷题笔记-1_第2张图片

解题:

力扣刷题笔记-1_第3张图片

如果我想计算红⾊的这个⼦矩阵的元素之和,可以⽤绿⾊矩阵减去蓝⾊矩阵减去橙⾊矩阵最后加上粉⾊矩 阵,⽽绿蓝橙粉这四个矩阵有⼀个共同的特点,就是左上⻆就是 (0, 0) 原点。 那么我们可以维护⼀个⼆维 preSum 数组,专⻔记录以原点为顶点的矩阵的元素之和,就可以⽤⼏次加减运 算算出任何⼀个⼦矩阵的元素和:

private int[][] preSum;
public void NumMatrix(int[][] matrix) {
    int m = matrix.length , n = matrix[0].length;
    
    if(m == 0 || n == 0){
        return;
    }
    //构造数组
    preSum = new int[m + 1][n + 1];
    
    for(int i = 1 ; i <= m; i++ ){
        for(int j = 1;j <= n; j++){
            // 计算每个矩阵[0,0,i,j]的元素和
            preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] + matrix[i - 1][j - 1] -preSum[i - 1][j - 1];
        }
    }
}

/**
 * 计算子矩阵[row1,col1,row2,col2] 的元素和
 */
public int sumRegion(int row1,int col1,introw2,int col2){
    // 目标矩阵之和有四个相邻矩阵运算获得
    return preSum[row2 + 1,col2 + 1] - preSum[row2+1][col1] - preSum[row1][col2 + 1] + preSum[row1][col1];
}
560. 和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

public int subarraySum(int[] nums,int k){
    int n = nums.length;
    // 构造前缀和
    int[] preSum = new int[n+1];
    
    for(int i = 1; i < n + 1 ; i++){
        preSum[i] = perSum[i-1] + nums[i-1];
    }
    
    // 记录满足条件子数组数
    int res = 0;
    
    // 穷举出所有子数组
    for(int i = 1 ; i <= n ;i++){
        for(int j = 0 ; j < i;j++){
            // 子数组 nums[j...i-1] 的元素和
            if(preSum[i] - preSum[j] == k){
                res++;
            }
        }
    }
  return res;
}

官方答案:

力扣刷题笔记-1_第4张图片

java:

public class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        for (int start = 0; start < nums.length; ++start) {
            int sum = 0;
            for (int end = start; end >= 0; --end) {
                sum += nums[end];
                if (sum == k) {
                    count++;
                }
            }
        }
        return count;
    }
}

go:

func subarraySum(nums []int, k int) int {
    n := len(nums)
    res := 0
	for i := 1; i <= n; i++ {
        sum := 0
		for j := i -1; j >= 0; j-- {
            sum += nums[j]
			if sum == k {
				res++
			}
		}
	}
	return res
}

差分数组

370. 区间加法(中等)

力扣刷题笔记-1_第5张图片

class Difference {
    // 差分数组
    private int[] diff;
    
    public Difference(int[] nums) {
		// 初始化差分数组
        diff = new int[nums.length];
        // 根据初始数组给差分数组赋值
        diff[0] = nums[0];
        for(int i = 0; i < nums.length ; i++){
            diff[i] = nums[i] - nums[i-1];
        }
    }
    
    /**
     * 给闭区间[i,j] 增加val
     */
    public void increment(int i,int j,int val){
        diff[i] += val;
        if(j+1 < diff.lenth){
            diff[j + 1] -= val;
        }
    }
    
    public int[] result(){
        int[] result = new int[diff.length]
        // 根据差分数组构造结果集
        result[0] = diff[0];
        for(int i = 1; i < diff.length;i++){
            result[i] = diff[i] + result[i-1]; 
        }
        return result;
    }
}
public int[] getModifiedArray(int length ,int[][] updates){
    // 初始化数组
    int[] nums = new int[lenrth];
    // 构造差分解法
    Difference diff = new Difference(nums);
    
    for(int[] update : updates){
        int i = update[0];
        int j = update[1];
        int val = update[2]
        diff.increment(i,j,val);
    }
    return diff.result();
}
1109. 航班预订统计(中等)
class Solution {

    // 定义差分数组
    private int[] diff;
    
    public int[] corpFlightBookings(int[][] bookings, int n) {
       diff = new int[n];
       diff[0] = 0;

       for(int[] booking:bookings){
           int i = booking[0] - 1;
           int j = booking[1] - 1;
           int val = booking[2];
           increment(i,j,val);
       }

        // 根据差分数组构造结果集
       int[] res = new int[n];
       res[0] = diff[0];
       for(int i=1;i < n ;i++){
           res[i] = res[i-1] + diff[i];
       }
       return res;   
    }

    /**
     * 给闭区间[i,j] 增加val
     */
    public void increment(int i,int j,int val){
        diff[i] += val;
        if(j+1 < diff.length){
            diff[j+1] -= val;
        }
    }
}

GO:

// 定义差分数组
var diff []int

func corpFlightBookings(bookings [][]int, n int) []int {
	diff = make([]int, n)

	for _, booking := range bookings {
		i := booking[0] - 1
		j := booking[1] - 1
		val := booking[2]
		increment(i, j, val)
	}

    // 根据差分数组构造结果集
	res := make([]int, n)
	res[0] = diff[0]
	for i := 1; i < n; i++ {
		res[i] = res[i-1] + diff[i]
	}
	return res

}

/**
 * 给闭区间[i,j] 增加val
 */
func increment(i, j, val int) {
	diff[i] += val
	if (j + 1) < len(diff) {
		diff[j+1] -= val
	}
}
1094. 拼车(中等)

解题思路:

把所有站看做一个数组,每位乘客所在的乘车区间为 [i,j] ,那么第 i 站到 j 站乘客就增加相应数量。记录每站车上的乘客数量,当某站的乘客超过 车的位置数量时就不能,反之则能。

class Solution {
    int[] diff;
    public boolean carPooling(int[][] trips, int capacity) {
        // 因为题目中有 trips.length <= 1000 ,所以...
        diff = new int[1001];
        diff[0] = 0;
        
		// 利用分差数组记录每站的乘客数量
        for(int[] trip: trips){
            int val = trip[0];
            int i = trip[1];
            int j = trip[2] -1;
            increment(i,j,val);
        }
        
        // 根据差分数组返回结果集,返回每站乘客的数量
        int[] res = new int[diff.length];
        res[0] = diff[0];
        for(int i = 1;i< diff.length;i++){
            res[i] = res[i-1] + diff[i];
        }

        for(int i =0 ; i <res.length;i++){
            // 如果某站乘客数量 超过 车的位置数 则不能
            if(capacity < res[i]){
                return false;
            }
        }
        return true;
    }

    // 增加 i 到 j 站的乘客数量
    public void increment(int i,int j,int val){
        diff[i] += val;
        if(j+1 < diff.length){
            diff[j+1] -= val;
        }
    }
}

GO:

var diff []int

func carPooling(trips [][]int, capacity int) bool {
	diff = make([]int, 1001)

	for _, trip := range trips {
		val := trip[0]
		i := trip[1]
		j := trip[2] - 1
		increment(i, j, val)
	}

	res := make([]int, 1001)
	res[0] = diff[0]
	for i := 1; i < len(diff); i++ {
		res[i] = res[i-1] + diff[i]
	}

	for i := 0; i < len(res); i++ {
		if capacity < res[i] {
			return false
		}
	}
	return true
}

func increment(i, j, val int) {
	diff[i] += val
	if (j + 1) < len(diff) {
		diff[j+1] -= val
	}
}
class Solution {
    Map<Character, Integer> ori = new HashMap<Character, Integer>();
    Map<Character, Integer> cnt = new HashMap<Character, Integer>();

    public String minWindow(String s, String t) {
        int tLen = t.length();
        for (int i = 0; i < tLen; i++) {
            char c = t.charAt(i);
            ori.put(c, ori.getOrDefault(c, 0) + 1);
        }
        int l = 0, r = -1;
        int len = Integer.MAX_VALUE, ansL = -1, ansR = -1;
        int sLen = s.length();
        while (r < sLen) {
            ++r;
            if (r < sLen && ori.containsKey(s.charAt(r))) {
                cnt.put(s.charAt(r), cnt.getOrDefault(s.charAt(r), 0) + 1);
            }
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                    ansR = l + len;
                }
                if (ori.containsKey(s.charAt(l))) {
                    cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1);
                }
                ++l;
            }
        }
        return ansL == -1 ? "" : s.substring(ansL, ansR);
    }

    public boolean check() {
        Iterator iter = ori.entrySet().iterator(); 
        while (iter.hasNext()) { 
            Map.Entry entry = (Map.Entry) iter.next(); 
            Character key = (Character) entry.getKey(); 
            Integer val = (Integer) entry.getValue(); 
            if (cnt.getOrDefault(key, 0) < val) {
                return false;
            }
        } 
        return true;
    }
}

双指针

快慢指针

141. 环形链表(简单)

判定链表中是否含有环

如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。

Java:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast,slow;
        fast = slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){
                return true;
            }
        }
        return false;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
 
func hasCycle(head *ListNode) bool {
	fast := head
	slow := head
	for fast != nil && fast.Next != nil {
		fast = fast.Next.Next
		slow = slow.Next

		if fast == slow {
			return true
		}
	}
	return false
}
142. 环形链表II(中等)

已知链表中含有环,返回这个环的起始位置

第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步:

力扣刷题笔记-1_第6张图片

fast 一定比 slow 多走了 k 步,这多走的 k 步其实就是 fast 指针在环里转圈圈,所以 k 的值就是环长度的「整数倍」

设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。

巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。你甭管 fast 在环里到底转了几圈,反正走 k 步可以到相遇点,那走 k - m 步一定就是走到环起点了:

力扣刷题笔记-1_第7张图片

所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。

Java:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast,slow;
        fast = slow = head;
        
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow ){
                break;
            }
        }

        if (fast == null || fast.next == null){
            return null;
        }

        slow = head;
        while (fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func detectCycle(head *ListNode) *ListNode {
    fast := head
    slow := head
    for fast != nil && fast.Next != nil{
        fast = fast.Next.Next
        slow = slow.Next
        if fast == slow {
            break
        }
    }

    if fast == nil || fast.Next == nil {
        return nil
    }

    slow = head
    for slow != fast{
        fast = fast.Next
        slow = slow.Next
    }
    return slow
}

左右指针

167. 两数之和 II - 输入有序数组(中等)

Java:

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0, right = numbers.length - 1;

        int sum = 0;
        while (left < right) {
            sum = numbers[right] + numbers[left];
            if (sum == target) {
                return new int[]{left + 1, right + 1};
            } else if (sum < target) {
                left++;
            } else if (sum > target) {
                right--;
            }
        }
        return new int[]{-1, -1};
    }
}

Go:

func twoSum(numbers []int, target int) []int {
	left := 0
	right := len(numbers) - 1
	sum := 0
	for left < right {
		sum = numbers[left] + numbers[right]
		if sum == target {
            // 题目要求的索引是从 1 开始的
			res := []int{left + 1, right + 1}
			return res
		} else if sum < target { // 让 sum 大一点
			left++
		} else if sum > target { // 让 sum 小一点
			right--
		}
	}
	return []int{-1, - 1}
}
344. 反转字符串(简单)

Java:

class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length -1;
        while(left < right){
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
        System.out.println(s);
    }
}

Go:

func reverseString(s []byte)  {
    left:= 0
    right := len(s) - 1
    for left < right {
        temp := s[left]
        s[left] = s[right]
        s[right] = temp
        left++
        right--
    }
    fmt.Println(s)
}

滑动窗⼝

76. 最小覆盖子串(困难)
class Solution {
    /**
     * 记录t中的所有字符,以及在s中出现的次数
     */
    Map<Character, Integer> needs = new HashMap<Character, Integer>();
    /**
     * 滑动窗口
     */
    Map<Character, Integer> window = new HashMap<Character, Integer>();

    public String minWindow(String s, String t) {
        int tLength = t.length();

        // 把t中的所有字符加入 needs中
        for (int i = 0; i < tLength; i++) {
            char c = t.charAt(i);
            needs.put(c, needs.getOrDefault(c, 0) + 1);
        }

        int left = 0, right = 0;
        // 窗⼝中满⾜ needs 条件的字符个数
        int valid = 0;
        // 记录最⼩覆盖⼦串的起始索引及⻓度
        int start = 0, len = Integer.MAX_VALUE;
        int sLength = s.length();
        while (right < sLength) {
            // c 是将移⼊窗⼝的字符
            char c = s.charAt(right);
            // 右移窗⼝
            right++;

            // 进⾏窗⼝内数据的⼀系列更新
            if (needs.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (window.get(c).equals(needs.get(c))) {
                    valid++;
                }
            }

            // 判断左侧窗⼝是否要收缩
            while (valid == needs.size()) {
                // 在这⾥更新最⼩覆盖⼦串
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                // d 是将移出窗⼝的字符
                char d = s.charAt(left);
                // 左移窗⼝
                left++;
                // 进⾏窗⼝内数据的⼀系列更新
                if (needs.containsKey(d)) {
                    if (window.get(d).equals(needs.get(d))) {
                        valid--;
                    }
                    window.put(d, window.getOrDefault(d, 0) - 1);
                }
            }
        }
        // 返回最⼩覆盖⼦串
        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
    }
}
func minWindow(s string, t string) string {
	need := make(map[string]int)
	windows := make(map[string]int)
	for i := 0; i < len(t); i++ {
		need[t[i:i+1]]++
	}

	left := 0
	right := 0
	valid := 0
	start := 0

	le := math.MaxInt32
	for right < len(s) {
		c := s[right : right+1]
		right++

		_, ok := need[c]
		if ok {
			windows[c]++
			if windows[c] == need[c] {
				valid++
			}
		}

		for valid == len(need) {

			if right-left < le {
				start = left
				le = right - left
			}

			d := s[left : left+1]

			left++
			_, ok := need[d]
			if ok {
				if windows[d] == need[d] {
					valid--
				}
				windows[d]--
			}
		}
	}

    if le == math.MaxInt32 {
		return ""
	}

	return s[start : start+le]
}
567. 字符串的排列(中等)

Java:

class Solution {
    /**
     * 记录t中的所有字符,以及在s中出现的次数
     */
    Map<Character, Integer> needs = new HashMap<Character, Integer>();
    /**
     * 滑动窗口
     */
    Map<Character, Integer> window = new HashMap<Character, Integer>();

    public boolean checkInclusion(String s1, String s2) {
        for (int i = 0; i < s1.length(); i++) {
            needs.put(s1.charAt(i), needs.getOrDefault(s1.charAt(i), 0) + 1);
        }
        int left = 0, right = 0;
        int valid = 0;
        while (right < s2.length()) {
            char c = s2.charAt(right);
            right++;
            if (needs.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (needs.get(c).equals(window.get(c))) {
                    valid++;
                }
            }

            while (right - left >= s1.length()) {
                if (valid == needs.size()) {
                    return true;
                }

                char d = s2.charAt(left);
                left++;
                if (needs.containsKey(d)) {
                    if (needs.get(d).equals(window.get(d))) {
                        valid--;
                    }
                    window.put(d, window.getOrDefault(d, 0) - 1);
                }

            }
        }
        return false;
    }
}

Go:

func checkInclusion(s1 string, s2 string) bool {
        need := make(map[string]int)
        windows := make(map[string]int)
    	// 把s1中的字符 放入need中
        for i:= 0 ; i < len(s1) ; i++ {
            need[s1[i:i+1]]++
        }

        left := 0
        right := 0
        valid := 0
        for right < len(s2) {
            c := s2[right:right+1]
            right++
            // 进行窗口内一系列更新
            _ ,ok := need[c]
            if ok {
                windows[c]++
                if need[c] == windows[c] {
                    valid++
                }
            }
			
            //  判断左窗口是否要收缩
            for right - left >= len(s1) {
                //  这里判断找到合法的字符串
                if valid == len(need) {
                    return true
                }
                
                d :=s2[left:left+1]
                left++
                
                // 进行窗口内一些列数据更新
                _ ,ok:=need[d]
                if ok {
                    if windows[d] == need[d]{
                        valid--
                    }
                    windows[d]--
                }
            }
        }
    	//  没有找到合法字符串
        return false
}
438. 找到字符串中所有字母异位词(中等)

Java:

class Solution {

    /**
     * 记录t中的所有字符,以及在s中出现的次数
     */
    Map<Character, Integer> needs = new HashMap<Character, Integer>();
    /**
     * 滑动窗口
     */
    Map<Character, Integer> window = new HashMap<Character, Integer>();

    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<Integer>();

        for (int i = 0; i < p.length(); i++) {
            needs.put(p.charAt(i), needs.getOrDefault(p.charAt(i), 0) + 1);
        }
        int left = 0, right = 0;
        int valid = 0;

        while (right < s.length()) {
            char c = s.charAt(right);
            right++;

            if (needs.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (needs.get(c).equals(window.get(c))) {
                    valid++;
                }
            }

            while (right - left >= p.length()) {
                if (valid == needs.size()) {
                    res.add(left);
                }

                char d = s.charAt(left);
                left++;
                if (needs.containsKey(d)) {
                    if (needs.get(d).equals(window.get(d))) {
                        valid--;
                    }
                    window.put(d, window.getOrDefault(d, 0) - 1);
                }
            }
        }
        return res;
    }
}

Go:

func findAnagrams(s string, p string) []int {
	need := make(map[string]int)
	window := make(map[string]int)
	var res []int
	for i := 0; i < len(p); i++ {
		need[p[i:i+1]]++
	}

	left := 0
	right := 0
	valid := 0

	for right < len(s) {
		c := s[right : right+1]
		right++
		_, ok := need[c]
		if ok {
			window[c]++
			if need[c] == window[c] {
				valid++
			}
		}

		for right - left >= len(p) {
			if valid == len(need) {
				res = append(res,left)
			}

			d := s[left : left+1]
			left++
			_, ok := need[d]
			if ok {
				if need[d] == window[d] {
					valid--
				}
				window[d]--
			}
		}
	}
	return res
}
3. 无重复字符的最长子串(中等)

Java:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> window = new HashMap<Character, Integer>();
        int left = 0, right = 0;
        int res = 0;
        while (right < s.length()) {
            char c = s.charAt(right);
            right++;

            window.put(c, window.getOrDefault(c, 0) + 1);
			
            // 如果新加入的字符使windon中的字符数量大于1了,就该收缩窗口了
            while (window.get(c) > 1) {
                char d = s.charAt(left);
                left++;
                window.put(d, window.getOrDefault(d, 0) - 1);
            }
            res = Math.max(res, right - left);
        }
        return res;
    }
}

Go:

func lengthOfLongestSubstring(s string) int {
	window := make(map[string]int)
    
	left := 0
	right := 0
	res := 0

	for right < len(s) {
		c := s[right : right+1]
		right++
		window[c]++

		for window[c] > 1 {
			d := s[left : left+1]
			left++
			window[d]--
		}
		res = int(math.Max(float64(res), float64(right-left)))
	}
	return res
}

⼆分搜索

寻找⼀个数(基本的⼆分搜索)

int binarySearch(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1; // 注意
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target)
            return mid;
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
    }
    return -1;
}

初始化right == nums.length - 1 , 相当于两端都闭区间 [left, right]

while (left <= right) ,终⽌的条件是left == right + 1

因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid+1 和 right = mid-1
因为我们只需找到⼀个 target 的索引即可
所以当 nums[mid] == target 时可以⽴即返回

寻找左侧边界的⼆分搜索

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意

    while (left < right) { // 注意
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    return left;
}

right = nums.length每次循环的「搜 索区间」是 [left, right) 左闭右开。

while(left < right) 终⽌的条件是left == right,此时搜索区间 [left, left) 为空,所以可以 正确终⽌。

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧右侧边界以锁定左侧边界

寻找右侧边界的⼆分查找

int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;

    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            left = mid + 1; // 注意
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left - 1; // 注意
}
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1
所以最后⽆论返回 left 还是 right,必须减⼀

总结:

// 基本二分搜索
int binary_search(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

// 寻找左侧的二分搜索
int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 最后要检查 left 越界的情况
    if (left >= nums.length || nums[left] != target) {
        return -1;
    }
    return left;
}

// 寻找右侧的二分搜索
int right_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定右侧边界
            left = mid + 1;
        }
    }
    // 最后要检查 right 越界的情况
    if (right < 0 || nums[right] != target) {
        return -1;
    }
    return right;
}
704. 二分查找(简单)

Java:

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

Go:

func search(nums []int, target int) int {
    left := 0
    right := len(nums) - 1
    for left <= right {
        mid:= left + (right - left) / 2
        if nums[mid] == target {
            return mid
        } else if target > nums[mid] {
            left = mid + 1
        }else if target < nums[mid] {
            right = mid - 1
        }
    }
    return -1
}
34. 在排序数组中查找元素的第一个和最后一个位置(中等)

Java:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int indexLeft = binarySearch(nums, target, true);
        int indexRight = binarySearch(nums, target, false);
        if (indexLeft >= nums.length || nums[indexLeft] != target || indexRight < 0 || nums[indexRight] != target) {
            return new int[]{-1, -1};
        }
        return new int[]{indexLeft, indexRight};
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        // lower  true 左   false右
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (target > nums[mid]) {
                left = mid + 1;
            } else if (target < nums[mid]) {
                right = mid - 1;
            } else if (target == nums[mid]) {
                if (lower) {
                    // 左
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
        }
       if (lower){
            return left;
        }
        return right;
    }
}

Go:

func searchRange(nums []int, target int) []int {
    leftIndex := binarySearch(nums,target,true)
    rightIndex := binarySearch(nums,target,false)

    if (leftIndex >= len(nums) || nums[leftIndex] != target || rightIndex < 0 || nums[rightIndex] != target){
        return []int{-1,-1}
    }
    return []int{leftIndex,rightIndex}
}

func binarySearch(nums []int, target int,flag bool) int {
    left := 0
    right := len(nums) - 1
    for left <= right {
        mid := left + (right - left)
        if target > nums[mid] {
            left = mid + 1
        }else if target < nums[mid]{
            right = mid - 1
        }else if target == nums[mid]{
            if flag {
                right = mid -1
            }else{
                left = mid + 1
            }
        }
    }

    if flag{
        return left
    }
    return right
}
875. 爱吃香蕉的珂珂(中等)

Java:

class Solution {
public int minEatingSpeed(int[] piles, int h) {
        int left = 1;
        int right = 1000000000 - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            // time 递减 当 time <= H 时应该左移 ,反之右移
            if (!possible(piles, h, mid)) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return left;
    }
	
    
    public boolean possible(int[] piles, int H, int K) {
        int time = 0;
        for (int pile : piles) {
            time += (pile - 1) / K + 1;
        }
        return time <= H;
    }
}

Go:

func minEatingSpeed(piles []int, h int) int {
    left := 1
    right := 1000000000 -1
    for left <= right {
        mid := left + (right - left) / 2
        if !possible(piles,h,mid) {
            left = mid + 1
        }else {
            right = mid -1
        }
    }
    return left
}

func possible(piles []int,h int,k int) bool {
    time:= 0
    for _,pile := range piles{
        time += (pile - 1) / k + 1
    }
    return time <= h
}
1011. 在D天内送达包裹的能力(中等)

因为船至少运走一件包裹,所以,left = weights中的最大值 ,最快一次运走,所以 right = weights中所有元素的总会。

Java:

class Solution {
    public int shipWithinDays(int[] weights, int days) {
        int left = Arrays.stream(weights).max().getAsInt(), right = Arrays.stream(weights).sum();
        while (left < right){
            int mid = left + (right - left ) / 2;
            // need 为需要运送的天数
            // sum 为当前这一天已经运送的包裹重量之和
            int need = 1,sum = 0;
            for (int weight : weights){
                if (sum + weight > mid){
                    need++;
                    sum = 0;
                }
                sum += weight;
            }

            if (need <= days){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        return left;
    }
}

Go:

func shipWithinDays(weights []int, days int) int {
	
	left := 0
	right := 0
	for _,w := range weights {
		if w > left {
			left = w
		}
		right += w
	}
	right--
	
	for left <= right {
		mid := left + (right - left ) / 2
		sum:= 0
		need := 1
		for _, weight :=range weights {
			if sum+weight > mid {
				need++
				sum = 0
			}
			sum += weight
		}
		
		if need <= days {
			right = mid - 1
		}else {
			left = mid + 1
		}
	}
	return left
}

田忌赛马背后的算法决策

870. 优势洗牌(中等)

Java:

class Solution {
    int[] advantageCount(int[] nums1, int[] nums2) {
    int n = nums1.length;
    // 给 nums2 降序排序
    PriorityQueue<int[]> maxpq = new PriorityQueue<>(
        (int[] pair1, int[] pair2) -> { 
            return pair2[1] - pair1[1];
        }
    );
    for (int i = 0; i < n; i++) {
        maxpq.offer(new int[]{i, nums2[i]});
    }
    // 给 nums1 升序排序
    Arrays.sort(nums1);

    // nums1[left] 是最小值,nums1[right] 是最大值
    int left = 0, right = n - 1;
    int[] res = new int[n];

    while (!maxpq.isEmpty()) {
        int[] pair = maxpq.poll();
        // maxval 是 nums2 中的最大值,i 是对应索引
        int i = pair[0], maxval = pair[1];
        if (maxval < nums1[right]) {
            // 如果 nums1[right] 能胜过 maxval,那就自己上
            res[i] = nums1[right];
            right--;
        } else {
            // 否则用最小值混一下,养精蓄锐
            res[i] = nums1[left];
            left++;
        }
    }
    return res;
}

}

有序数组/链表去重

26. 删除有序数组中的重复项

让慢指针 slow ⾛在后⾯,快指针 fast ⾛在前⾯探路,找到⼀个不重复的元素就告诉 slow 并让 slow 前进⼀步。这样当 fast 指针遍历完整个数组 nums 后,nums[0…slow] 就是不重复元素。

class Solution {
    public int removeDuplicates(int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        int slow = 0,fast = 0;
        while(fast < nums.length){
            if(nums[slow] != nums[fast]){
                slow++;
                nums[slow] = nums[fast];
            }
            fast++;
        }
        return slow + 1;
    }
}

Go:

func removeDuplicates(nums []int) int {
    if len(nums) == 0 {
        return 0
    }
    slow:= 0
    fast := 0
    for fast < len(nums) {
        if nums[slow] != nums[fast] {
            slow++
            nums[slow] = nums[fast]
        }
        fast++
    }
    return slow + 1
} 
83. 删除排序链表中的重复元素

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode slow ,fast;
        slow = fast = head;
        while(fast != null){
            if(slow.val != fast.val){
                slow.next = fast;
                slow = slow.next;
            }
            fast = fast.next;
        }
        slow.next = null;
        return head;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func deleteDuplicates(head *ListNode) *ListNode {
    if head == nil {
        return nil;
    }
    slow := head
    fast := head
    for fast != nil {
        if slow.Val != fast.Val {
            slow.Next = fast
            slow = slow.Next
        }
        fast = fast.Next
    }
    slow.Next = nil
    return head
}
27. 移除元素

Java:

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

Go:

func removeElement(nums []int, val int) int {
        slow := 0
        fast := 0
        for fast < len(nums) {
            if val != nums[fast] {
                nums[slow] = nums[fast]
                slow++
            }
            fast++
        }
        return slow
}
283. 移动零

Java:

class Solution {
	public void moveZeroes(int[] nums) {
		if(nums==null) {
			return;
		}
		//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统都赋给nums[j]
		int j = 0;
		for(int i=0;i<nums.length;++i) {
			if(nums[i]!=0) {
				nums[j++] = nums[i];
			}
		}
		//非0元素统计完了,剩下的都是0了
		//所以第二次遍历把末尾的元素都赋为0即可
		for(int i=j;i<nums.length;++i) {
			nums[i] = 0;
		}
	}
}	
class Solution {
    public void moveZeroes(int[] nums) {
        //两个指针i和j
        int j = 0;
        for (int i = 0; i < nums.length ; i++){
            //当前元素!=0,就把其交换到左边,等于0的交换到右边
            if (nums[i] != 0){
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                j++;
            }
        }
    }

}
class Solution {
    void moveZeroes(int[] nums) {
        // 去除 nums 中的所有 0
        // 返回去除 0 之后的数组⻓度
        int p = removeElement(nums, 0);
        // 将 p 之后的所有元素赋值为 0
        for (; p < nums.length; p++) {
            nums[p] = 0;
        }
    }

	// 移除 nums 中值为 val 的所有值 返回nums移除目标值后的长度
    public int removeElement(int[] nums, int val) {
        int slow = 0, fast = 0;
        while (fast < nums.length) {
            if (val != nums[fast]) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}  

单链表的六⼤解题套路

21. 合并两个有序链表

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode temp = new ListNode(-1);
        ListNode p1 = list1,p2 = list2,p = temp;

        while(p1 != null && p2 != null){
            if (p1.val < p2.val){
                p.next = p1;
                p1 =p1.next;
            }else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }

        if (p1 != null){
            p.next = p1; 
        }

        if (p2 != null){
            p.next =p2;
        }

        return temp.next;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func mergeTwoLists(p1 *ListNode, p2 *ListNode) *ListNode {
	var preHead = new(ListNode)
	var prev *ListNode

	prev = preHead

   for p1 !=nil && p2 !=nil{
       if p1.Val < p2.Val {
           prev.Next = p1
           p1 = p1.Next
       }else {
           prev.Next = p2
           p2 = p2.Next
       }
       prev = prev.Next
   }

   if p1 !=nil{
       prev.Next = p1
   }

   if p2 != nil {
       prev.Next = p2
   }
   return preHead.Next
   
}
23. 合并K个升序链表

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        // 虚拟头结点
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;

        // 优先队列 最小堆
        PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>(
                lists.length, (a, b) -> (b.val - a.val));

        // 将k个链表的头结点加入最小堆
        for (ListNode head : lists) {
            if (head != null) {
                priorityQueue.add(head);
            }
        }

        while (!priorityQueue.isEmpty()) {
            ListNode node = priorityQueue.poll();
            p.next = node;
            if (node.next != null) {
                priorityQueue.add(node.next);
            }
            p = p.next;
        }
        return dummy.next;

    }
}
19. 删除链表的倒数第 N 个结点

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast,slow,temp;
        fast = slow = head;
        temp = head;

         // 快指针先前进 n 步
        for (int i = 0 ; i < n ;i++){
            fast = fast.next;
        }

        if (fast == null){
            // 如果此时快指针走到头了,
            // 说明倒数第 n 个节点就是第一个节点
            return head.next;
        }

         // 让慢指针和快指针同步向前
        while (fast != null && fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        
        // slow.next 就是倒数第 n 个节点,删除它
        slow.next = slow.next.next; 
        return head;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    fast := head
    slow := head
    
    for i:= 0;i<n;i++ {
        fast = fast.Next
    }

    if fast == nil {
        return head.Next
    }

    for fast != nil && fast.Next != nil {
        fast = fast.Next
        slow = slow.Next
    }

    slow.Next = slow.Next.Next
    return head
}
876. 链表的中间结点

Java: 快慢指针

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast,slow;
        fast = slow = head;
        while (fast != null && fast.next !=null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func middleNode(head *ListNode) *ListNode {
  fast := head
  slow := head
  for fast != nil && fast.Next != nil{
      fast = fast.Next.Next
      slow = slow.Next
  }
  return slow
}
160. 相交链表

力扣刷题笔记-1_第8张图片

Java:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pa = headA,pb = headB;

        while (pa != pb){
            if (pa == null){
                pa = headB;
            }else {
                pa = pa.next;
            }

            if (pb == null){
                pb = headA;
            }else {
                pb = pb.next;
            }
            
        }

        return pa;

    }
}

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    pa := headA
    pb := headB
    for pa != pb {
        if pa == nil {
            pa = headB
        }else {
            pa = pa.Next
        }

        if pb == nil {
            pb = headA
        }else {
            pb = pb.Next
        }
    }
        return pb
}

链表操作的递归思维

206. 反转链表

方法一:重新创造个链表,把目标链表一个一个的插入新的链表头结点后面

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode n = new ListNode(-1);
        ListNode p,temp;
        p = head;
        while (p != null){
            temp = p;
            p = p.next;
            temp.next = n.next;
            n.next = temp;
        }
        return n.next;
    }
}

方法二:递归

力扣刷题笔记-1_第9张图片

力扣刷题笔记-1_第10张图片

力扣刷题笔记-1_第11张图片

力扣刷题笔记-1_第12张图片

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */

class Solution {
    public ListNode reverseList(ListNode head) {
      if (head == null || head.next == null){
          return head;
      }
      
      ListNode last = reverseList(head.next);
      head.next.next = head;
      head.next = null;
        
      return last;
    }
}
92. 反转链表 II

方法一:递归

Java:

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
       ListNode successor = null; // 后驱节点

    public ListNode reverseN(ListNode head, int n) {
        if (n == 1) {
            // 记录第 n + 1 个结点
            successor = head.next;
            return head;
        }

        // 以head.next 为起点,需要反转前 n - 1 个结点
        ListNode last = reverseN(head.next, n - 1);

        head.next.next = head;
        // 让反转之后的 head 节点和后面的节点连起来
        head.next = successor;
        return last;
    }

    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (left == 1) {
            return reverseN(head, right);
        }
        head.next = reverseBetween(head.next, left - 1, right - 1);
        return head;
    }
}

方法二:

public ListNode reverseBetween(ListNode head, int left, int right) {
    // 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
    ListNode dummyNode = new ListNode(-1);
    dummyNode.next = head;

    ListNode pre = dummyNode;
    // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
    // 建议写在 for 循环里,语义清晰
    for (int i = 0; i < left - 1; i++) {
        pre = pre.next;
    }

    // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
    ListNode rightNode = pre;
    for (int i = 0; i < right - left + 1; i++) {
        rightNode = rightNode.next;
    }

    // 第 3 步:切断出一个子链表(截取链表)
    ListNode leftNode = pre.next;
    ListNode curr = rightNode.next;

    // 注意:切断链接
    pre.next = null;
    rightNode.next = null;

    // 第 4 步:同第 206 题,反转链表的子区间
    reverseList(leftNode);

    // 第 5 步:接回到原来的链表中
    pre.next = rightNode;
    leftNode.next = curr;
    return dummyNode.next;
}

队列/栈

232. 用栈实现队列

Java:

力扣刷题笔记-1_第13张图片

class MyQueue {

    Stack<Integer> s1, s2;
    public MyQueue() {
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    
    public void push(int x) {
        s1.push(x);
    }
    
     /**
     * 出队:
     * 删除队头元素并返回
     */
    public int pop() {
        peek();
        return s2.pop();
    }
    
    /**
     * 返回队头元素
     */
    public int peek() {
        // 把s1 元素 压入 s2
        if (s2.isEmpty()){
            while (!s1.isEmpty()) {
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */
225. 用队列实现栈

Java:

力扣刷题笔记-1_第14张图片

力扣刷题笔记-1_第15张图片

class MyStack {

    Queue<Integer> q = new LinkedList<>();
    int top_elem = 0;

    public MyStack() {

    }

    /**
     * 添加元素到栈顶
     */
    public void push(int x) {
        // x是队尾 ,同时也是栈顶
        q.offer(x);
        top_elem = x;
    }

    /**
     * 删除栈顶的元素并返回
     */
    public int pop() {
        int size = q.size();
        while (size > 2) {
            q.offer(q.poll());
            size--;
        }
        // 记录新的队尾元素
        top_elem = q.peek();
        q.offer(q.poll());
        return q.poll();
    }

    /**
     * 返回栈顶元素
     */
    public int top() {
        return top_elem;
    }

    /**
     * 判断栈是否为空
     */
    public boolean empty() {
        return q.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

解决括号相关的问题

20. 有效的括号(简单)

Java:

class Solution {
public boolean isValid(String s) {
        char[] chars = s.toCharArray();
       Stack<Character> left = new Stack<>();
        for (char c : chars) {
            if (c == '(' || c == '[' || c == '{') {
                left.push(c);
            } else {
                // 字符 c 是右括号
                if (!left.empty() && leftOf(c) == left.peek()) {
                    left.pop();
                } else {
                    // 和最近的左括号不匹配
                    return false;
                }
            }
        }
        return left.empty();
    }

    char leftOf(char c) {
        if (c == ')') {
            return '(';
        }

        if (c == ']') {
            return '[';
        }

        return '{';
    }
}
921. 使括号有效的最小添加(中等)

Java:

class Solution {
    public int minAddToMakeValid(String s) {
        char[] chars = s.toCharArray();
        // res 记录插入次数
        int res = 0;
        // need 记录右括号的需求量
        int need = 0;
        for (int i = 0; i < s.length(); i++) {
            if (chars[i] == '(') {
                need++;
            }

            if (chars[i] == ')') {
                need--;
            }

            if (need == -1) {
                need = 0;
                res++;
            }
        }
        return res + need;
    }
}
1541. 平衡括号串的最少插入(中等)

Java:

class Solution {
    public int minInsertions(String s) {
        char[] chars = s.toCharArray();
        int res = 0, need = 0;
        for (int i = 0; i < s.length(); i++) {
            if (chars[i] == '(') {
                need += 2;
                if (need % 2 == 1) {
                    // 插入一个右括号
                    res++;
                    // 对右括号的需求减一
                    need--;
                }
            }

            if (chars[i] == ')') {
                need--;
                // 说明右括号太多了
                if (need == -1) {
                    // 需要插入一个左括号
                    res++;
                    // 同时,对右括号的需求变为 1
                    need = 1;
                }
            }
        }

        return res + need;
    }
}

单调栈结构

解题思想

496. 下一个更大元素I(简单)

Java:迭代法

class Solution {
       public int[] nextGreaterElement(int[] nums1, int[] nums2) {
		// 结果数组
        int[] result = new int[nums1.length];
		// 标记num2 中 对应的位置
        boolean flag = false;
        for (int i = 0; i < nums1.length; i++) {
            // 先设置为-1 没找到
            result[i] = -1;
            for (int j = 0; j < nums2.length; j++) {
                if (nums2[j] == nums1[i]) {
                    // 找到对应位置
                    flag = true;
                    continue;
                }
                if (flag && nums2[j] > nums1[i]) {
                    // 找到赋值
                    result[i] = nums2[j];
                    break;
                }
            }
            // 找一个元素前标记重置
            flag = false;
        }
        return result;
    }
}
739. 每日温度

Java:

解题

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] result = new int[temperatures.length];

        Stack<Integer> stack = new Stack<>();

        for (int i = temperatures.length - 1; i >= 0; i--) {

            while (!stack.empty() && temperatures[stack.peek()] <= temperatures[i]) {
                stack.pop();
            }

            result[i] = stack.empty() ? 0 : stack.peek() - i;
            stack.push(i);
        }
        return result;
    }
}
503. 下一个更大元素 II

Java:

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] result = new int[nums.length];

        Stack<Integer> stack = new Stack<>();
        for (int i = 2 * nums.length - 1; i >= 0; i--) {
            while (!stack.empty() && stack.peek() <= nums[i % nums.length]) {
                stack.pop();
            }

            result[i%nums.length] = stack.empty() ? -1 : stack.peek();
            stack.push(nums[i % nums.length]);
        }
        return result;
    }
}

单调队列结构解决滑动窗口

239. 滑动窗口最大值(困难)

常数时间,删除/查找数组中的任意元素

380. O(1) 时间插入、删除和获取随机元素

Java:

class RandomizedSet {

    public RandomizedSet() {
       
    }
    
    List<Integer> list = new ArrayList<Integer>();
    Map<Integer, Integer> map = new HashMap<>();

    public boolean insert(int value) {
        // 若value 已经存在 ,不再插入
        if (map.containsKey(value)) {
            return false;
        }

        // 若value不存在,插入nums的尾部
        // 并记录value对应的索引
        map.put(value, list.size());
        list.add(value);
        return true;
    }

    public boolean remove(int value) {
         // 若value不存在,不在删除
        if (!map.containsKey(value)) {
            return false;
        }
        // 先拿到value索引
        int index = map.get(value);
        //  交换到尾部
        map.put(value, list.size() - 1);
        map.put(list.get(list.size() - 1), index);

        int temp = list.get(list.size() - 1);
        list.set(list.size() - 1, value);
        list.set(index, temp);

        // 删除
        map.remove(value);
        list.remove(list.size() - 1);
        return true;
    }

    public int getRandom() {
        Random random = new Random();
        int x = random.nextInt(list.size());
        return list.get(x);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

] result = new int[temperatures.length];

    Stack stack = new Stack<>();

    for (int i = temperatures.length - 1; i >= 0; i--) {

        while (!stack.empty() && temperatures[stack.peek()] <= temperatures[i]) {
            stack.pop();
        }

        result[i] = stack.empty() ? 0 : stack.peek() - i;
        stack.push(i);
    }
    return result;
}

}




###### [503. 下一个更大元素 II](https://leetcode-cn.com/problems/next-greater-element-ii/)

`Java:`

```java
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] result = new int[nums.length];

        Stack stack = new Stack<>();
        for (int i = 2 * nums.length - 1; i >= 0; i--) {
            while (!stack.empty() && stack.peek() <= nums[i % nums.length]) {
                stack.pop();
            }

            result[i%nums.length] = stack.empty() ? -1 : stack.peek();
            stack.push(nums[i % nums.length]);
        }
        return result;
    }
}

单调队列结构解决滑动窗口

239. 滑动窗口最大值(困难)

常数时间,删除/查找数组中的任意元素

380. O(1) 时间插入、删除和获取随机元素

Java:

class RandomizedSet {

    public RandomizedSet() {
       
    }
    
    List<Integer> list = new ArrayList<Integer>();
    Map<Integer, Integer> map = new HashMap<>();

    public boolean insert(int value) {
        // 若value 已经存在 ,不再插入
        if (map.containsKey(value)) {
            return false;
        }

        // 若value不存在,插入nums的尾部
        // 并记录value对应的索引
        map.put(value, list.size());
        list.add(value);
        return true;
    }

    public boolean remove(int value) {
         // 若value不存在,不在删除
        if (!map.containsKey(value)) {
            return false;
        }
        // 先拿到value索引
        int index = map.get(value);
        //  交换到尾部
        map.put(value, list.size() - 1);
        map.put(list.get(list.size() - 1), index);

        int temp = list.get(list.size() - 1);
        list.set(list.size() - 1, value);
        list.set(index, temp);

        // 删除
        map.remove(value);
        list.remove(list.size() - 1);
        return true;
    }

    public int getRandom() {
        Random random = new Random();
        int x = random.nextInt(list.size());
        return list.get(x);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

Study:
labuladong算法小抄

你可能感兴趣的:(算法练习,java,算法)