个人算法刷题笔记(CodeTop)

本文为自己为秋招准备的刷题记录,题目列表来自CodeTop

leetcode 206. 反转链表

题目描述:
在这里插入图片描述
解决思路:

1、头插法:每次遍历将该节点指向上一节点,不断重复最终达成反转链表的目的,其中时间复杂度为O(N),应该是最简单的思路

代码:

class Solution {
    
    
    public ListNode reverseList(ListNode head) {
    
    
        ListNode res = null;
        while(head != null) {
    
    
            ListNode temp = head.next;
            head.next = res;
            res = head;
            head = temp;
        }
        return res;
    }
}

leetcode 3. 无重复字符的最长子串

题目描述:
在这里插入图片描述
解决思路
1、暴力解法,遍历所有元素,用一个List存储元素,若出现连续元素,记录当前List大小,更新max值。若未出现连续元素,增加进List。此时时间复杂度为O(N^2)

代码:

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        if (s.length() <= 1)  return s.length();
        List<Character> chars = new ArrayList<>();
        char []str = s.toCharArray();
        int max = 0;
        for(int i = 0; i < str.length; i ++) {
    
    
            for(int j = i; j < str.length; j ++) {
    
    
                if (!chars.contains(str[j])) {
    
    
                    chars.add(str[j]);
                } else {
    
    
                    max = max > chars.size() ? max : chars.size();
                    chars.clear();
                    break; 
                }
            }
        }
        return max;
    }
}

2、采用滑动窗口的思想来做,采用两个指针,一个指向无重复序列的左边界,一个指向无重复序列的右边界。不断遍历到最后,理论上可以达到O(N)的时间复杂度。

import java.util.*;
class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        if (s.length() <= 1)    return s.length(); 
        HashMap<Character, Integer> chars = new HashMap<>();			//用一个HashMap来存储数据
        int left = 0;
        int right = 0;
        int max = 0;
        chars.put(s.charAt(0), 0);
        for (int i = 1; i < s.length(); i ++) {
    
    
            right ++;
            if (chars.get(s.charAt(i)) == null) {
    
    
                chars.put(s.charAt(i), i);                 
            } else {
    
    
                left = left > chars.get(s.charAt(i)) ? left : chars.get(s.charAt(i)) + 1;
                chars.put(s.charAt(i), i);    
            }
            max = right - left + 1 > max ? right - left + 1 :max;
        }
        return max;
    }
}
class LRUCache {
    
    
    int capacity;
    LinkedHashMap<Integer, Integer> list = new LinkedHashMap();
    public LRUCache(int capacity) {
    
    
        list = new LinkedHashMap<>(capacity);
        this.capacity = capacity;
    }
    
    public int get(int key) {
    
    
        if (list.get(key) == null)  return -1;
        makeFirst(key);
        return list.get(key);
    }
    
    public void put(int key, int value) {
    
    
        if (list.get(key) != null) {
    
    
            makeFirst(key);
            list.put(key, value);
        } else {
    
    
            if (list.size() >= capacity) {
    
    
            list.remove(list.keySet().iterator().next());
            }
            list.put(key, value);
        }

    }

    public void makeFirst(int key) {
    
    
        int val = list.remove(key);
        list.put(key, val);
    }
}

leetcode 146. LRU缓存机制

题目描述:
在这里插入图片描述

思路:
采用LinkedHashMap来做,采用一个makefirst函数,将每次获取或者put新值的数据放到链表末端。

代码:

class LRUCache {
    
    
    int capacity;
    LinkedHashMap<Integer, Integer> list = new LinkedHashMap();
    public LRUCache(int capacity) {
    
    
        list = new LinkedHashMap<>(capacity);
        this.capacity = capacity;
    }
    
    public int get(int key) {
    
    
        if (list.get(key) == null)  return -1;
        makeFirst(key);
        return list.get(key);
    }
    
    public void put(int key, int value) {
    
    
        if (list.get(key) != null) {
    
    
            makeFirst(key);
            list.put(key, value);
        } else {
    
    
            if (list.size() >= capacity) {
    
    
            list.remove(list.keySet().iterator().next());
            }
            list.put(key, value);
        }

    }
	//删除源节点,放到链表末端。
    public void makeFirst(int key) {
    
    
        int val = list.remove(key);
        list.put(key, val);
    }
}

leetcode 215. 数组中的第K个最大元素

题目描述:
在这里插入图片描述
思路:
1、采用排序,将数组排序后输出目标元素,时间复杂度由排序算法决定,此处为NO(logN):

class Solution {
    
    
    public int findKthLargest(int[] nums, int k) {
    
    
        quickSort(nums, 0, nums.length - 1);
        return nums[nums.length - k];
    }
    public void quickSort(int [] nums, int l, int r) {
    
    
        if (l >= r )    return ;
        int target = nums[l];
        int i = l;
        int j = r + 1;
        while (i < j) {
    
    
            while (i < r && nums[++ i] < target);
            while (j > l && nums[-- j] > target);
            if(i > j)   break;
            swap(nums, i, j);
        }
        nums[l] = nums[j];
        nums[j] = target;
        quickSort(nums, l, j - 1);
        quickSort(nums, j + 1, r);
    }
    public void swap(int [] nums, int i, int j) {
    
    
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

2、采用优先队列,维护一个数量为K的小顶堆优先队列,最后弹出栈顶,时间复杂度为NO(logK)

代码:

class Solution {
    
    
    public int findKthLargest(int[] nums, int k) {
    
    
    	//o1 - o1 = 升序,也就是小顶堆,O2 - O1 :降序,也就是大顶堆,降序
        PriorityQueue<Integer> queue = new PriorityQueue<>((o1,o2) -> o1 - o2);
        int i = 0;
        for (; i < k; i ++) {
    
    
            queue.offer(nums[i]);
        }
        for (; i < nums.length; i ++) {
    
    
            if (queue.peek() < nums[i]) {
    
    
                queue.poll();
                queue.offer(nums[i]);
            }
        }
        return queue.peek();
    }
}

leetcode 25. K 个一组翻转链表

描述:
在这里插入图片描述
思路:
使用快慢指针,慢指针指向要反转部分的前一个节点,快指针指向要翻转的最后一个节点。
遍历到快指针位置后,将快指针与前面部分打断,然后将慢指针到快指针这部分进行反转链表。
再将反转后的部分链表接回到原来的位置。也就是将反转后的头部与慢指针相接,尾部与快指针的下一个节点相接。

时间复杂度为K * O(K)

代码:

class Solution {
    
    
    public ListNode reverseKGroup(ListNode head, int k) {
    
    
        if (head == null || head.next == null)   return head;
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode slow = res;
        ListNode fast = res;
        int n = k;
        while (n > 0) {
    
    
            n --;
            fast = fast.next;
            if (fast == null) {
    
    
                break;
            }
            if (n == 0) {
    
    
                ListNode next = fast.next;		//记录下一节点
                ListNode pre = slow.next;		//记录要反转的第一个节点
                fast.next = null;				//打断
                slow.next = reverse(pre);		//反转,头部用slow.next接回
                pre.next = next;				//pre变成反转后的最后一个,接到下一个
                slow = pre;						//回到pre的位置
                fast = pre;
                n = k;
            }
        }
        return res.next;
    }
    public ListNode reverse(ListNode head) {
    
    
        ListNode res = null;
        while(head != null) {
    
    
            ListNode temp = head.next;
            head.next = res;
            res = head;
            head = temp;
        }
        return res;
    }
}

leetcode 补充题4. 手撕快速排序

题目描述:
在这里插入图片描述
思路:
本题主要考察的是快排的写法,有两种,一种是基于交换的快排,一种是堆排序。

对于基于交换的快排,其思路是定下某个标兵,而后将该标兵为准,左侧右侧按照大于或者小于的顺序进行排列,直到数组有序。
时间复杂度为N * O (LogN).

若每个标兵选取时均为边界元素,则会出现最坏情况,时间复杂度退回到O( N^2)。解决该情况可以采用随机化标兵。

代码:

class Solution {
    
    
    public int[] sortArray(int[] nums) {
    
    
        Arrays.sort(nums);
        return nums;
    }
    public void quickSort(int nums[], int l, int r) {
    
    
        if (l >= r)     return;
        int temp = nums[l];
        int i = l;
        int j = r + 1;
        while (i < j) {
    
    
            while (i < r && nums[++ i] < temp);
            while (j > l && nums[-- j] > temp);
            if (i >= j) break;
            swap(nums, i, j);
        }
        nums[l] = nums[j];
        nums[j] = temp;
        quickSort(nums, j + 1, r);
        quickSort(nums, l, j - 1);
    }
    public void swap(int [] nums,int i, int j) {
    
    
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

leetcode 1. 两数之和

在这里插入图片描述
思路:
1、采用暴力思路,遍历所有情况,若出现有满足要求的数值,将两个下标作为结果返回。时间复杂度为O (N^2)
代码略。

2、一次遍历,在遍历过程中将值与下标作为key - value存储,遍历到下一个元素时,判断是否出现target - num[i] 的key值,若存在,则说明找到满足该情况的下标组合,将其作为结果返回。时间复杂度不算HashMap存储的情况可以达到O(N)。
代码:

class Solution {
    
    
    public int[] twoSum(int[] nums, int target) {
    
    
        int res [] = new int [2];
        HashMap<Integer, Integer> temp = new HashMap<>();
        temp.put(nums[0], 0);
        for (int i = 1; i < nums.length; i ++) {
    
    
            if(temp.containsKey(target - nums[i])) {
    
    
                res[0] = temp.get(target - nums[i]);
                res[1] = i;
                break;
            }
            temp.put(nums[i], i);
        }
        return res;
    }
}

leetcode 15. 三数之和

题目描述:
在这里插入图片描述
思路:
1、将数组进行排序,然后定下第1个元素,用左右指针进行遍历,其中左指针指向剩余数字的最小值,右指针指向剩余数组的最大值。不断移动寻找符合情况的元素,找到则返回三个数值。

代码:

import java.util.*;
class Solution {
    
    
    public static  List<List<Integer>> threeSum(int[] nums) {
    
    
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (nums.length < 3)    return res;
        Arrays.sort(nums);
        System.out.print(Arrays.toString(nums));
        for (int i = 0; i < nums.length - 2; i ++) {
    
    
            if (nums[i] > 0)    break;
            int left = i + 1;
            int right = nums.length - 1;
            boolean flag = false;
            while (left < right) {
    
    
                if (nums[left] + nums[right] + nums[i] == 0) {
    
    
                    res.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right])));
                    if (flag == false) flag = !flag;
                    while (++ left < right && nums[left] == nums[left - 1]);        //去重
                    while (-- right > left && nums[right] == nums[right + 1]);      //去重
                } else if (nums[left] + nums[right] + nums[i] > 0) {
    
    
                    right --;
                } else if (nums[left] + nums[right] + nums[i] < 0){
    
    
                    left ++;
                }
            }
            if (flag) {
    
    
                while (++ i < nums.length - 2 && nums[i] == nums[i - 1]);
                i --;           //这里后续I++多了一次,因此要进行 --操作。
            }
        }
        return res;
}
}

leetcode 53. 最大子序和

题目描述:
在这里插入图片描述

思路:
1、暴力法遍历所有情况,记录下每个区间的累计和大小,用一个max记录最大值,最后遍历完返回。时间复杂度为 O (N ^ 2)。

代码略。

2、通过动态规划的思想,开辟一个最大子序和数组,其中每个值为遍历到该位的最大子序和。其动态规划方程为:
dp[i] = dp[i - 1] + nums[i] > nums[i] ? dp[i - 1] + nums[i] : nums[i];
用一个max记录遍历过程中的最大值,返回。

时间复杂度为O(N)

代码:

class Solution {
    
    
    public int maxSubArray(int[] nums) {
    
    
        if (nums.length == 1)   return nums[0];
        int dp [] = new int[nums.length];
        int res = nums[0];
        dp[0] = nums[0];
        for (int i = 1; i < dp.length; i ++) {
    
    
            dp[i] = dp[i - 1] + nums[i] > nums[i] ? dp[i - 1] + nums[i] : nums[i];		//切记比较方是nums[i]
            res = dp[i] > res ? dp[i] : res;
        }
        return res;
    }
}

leetcode 21. 合并两个有序链表

题目描述:
在这里插入图片描述
思路:
1、用递归来做,暂时不会。

2、用迭代来做,也就是逐个比较,小的节点放在前,大的节点放在后,若有一方为空,则将另一方直接接在最后。时间复杂度为O (两个链表节点之和)

代码:

class Solution {
    
    
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    
    
        if (l1 == null && l2 == null)   return null;
        if (l1 == null)                 return l2;
        if (l2 == null)                 return l1;
        ListNode res = new ListNode(0);
        ListNode head = res;
        while (l1 != null && l2 != null) {
    
    
            if (l1.val < l2.val) {
    
    
                res.next = l1;
                l1 = l1.next;
            } else {
    
    
                res.next = l2;
                l2 = l2.next;
            }
            res = res.next;
        }
        res.next = l1 == null ? l2 : l1;
        return head.next;
    }
}

leetcode 141. 环形链表

题目描述:
在这里插入图片描述
思路:
1、通过List存储该节点,若在遍历过程中发现该节点已经存在,说明存在环节点。时间复杂度为O(N)* List.contains()。

代码:

public class Solution {
    
    
    public boolean hasCycle(ListNode head) {
    
    
        if (head == null || head.next == null)  return false;
        List<ListNode> temp = new ArrayList<>();
        while (head.next != null) {
    
    
            if (temp.contains(head))    return true;
            temp.add(head);
            head = head.next;
        }
        return false;
    }
}

2、通过快慢指针,快指针一次走两步,慢指针一次走一步,二者相差一步,若二者相遇,则说明存在环,若不相遇,说明不存在环。

代码:

public class Solution {
    
    
    public boolean hasCycle(ListNode head) {
    
    
        if (head == null || head.next == null)  return false;
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next != null) {
    
    
            fast = fast.next.next;
            if (fast == null) {
    
    
                return false;
            }
            if (fast == slow) {
    
    
                return true;
            }
            slow = slow.next;
        }
        return false;
    }
}

leetcode 160. 相交链表

题目描述:
在这里插入图片描述
思路:

1、使用一个List将一条链表节点存储,而后将第二条链表遍历,比较该节点是否在List中,若在说明该点相交节点,若遍历完未找到相交节点,说明没有相交。
代码:

public class Solution {
    
    
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
        List<ListNode> temp = new ArrayList<>();
        while (headA != null) {
    
    
            temp.add(headA);
            headA = headA.next;
        }
        while (headB != null) {
    
    
            if (temp.contains(headB))   return headB;
            headB = headB.next;
        }
        return null;
    }
}

2、根据节点的前缀和关系,有以下代码:
解释详见:题解

代码:

public class Solution {
    
    
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
        ListNode l1 = headA;
        ListNode l2 = headB;
        while (l1 != l2) {
    
    
            l1 = l1 == null ? headB : l1.next;
            l2 = l2 == null ? headA : l2.next; 
        }
        return l1;
    }
}

leetcode 102. 二叉树的层序遍历

题目描述:

在这里插入图片描述
思路:
用两个队列来辅助左DFS,其中一个存储层次的节点,一个用来存储下一层的节点,每遍历完一层之后,将下一层点移动到这一层点,再将本层点放到结果集合。

代码:

class Solution {
    
    
    public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new ArrayList<List<Integer>>(); 
        List<Integer> tempNums = new ArrayList<Integer>();
        if(root == null) return res;
        Queue<TreeNode> temp = new LinkedList<TreeNode>();
        Queue<TreeNode> next = new LinkedList<TreeNode>();
        temp.offer(root);
        tempNums.add(root.val);
        res.add(new ArrayList<Integer>(tempNums));
        tempNums.clear();
        while(!temp.isEmpty()) {
    
    
            TreeNode tempNode = temp.poll();
            if (tempNode.left != null) {
    
    
                next.add(tempNode.left);
                tempNums.add(tempNode.left.val);
            }
            if (tempNode.right != null) {
    
    
                next.add(tempNode.right);
                tempNums.add(tempNode.right.val);
            }
            if (temp.isEmpty()) {
    
    
                if(tempNums.size() > 0){
    
    
                res.add(new ArrayList<Integer>(tempNums));
                tempNums.clear();
                temp = next;
                next = new LinkedList<TreeNode>();
                }
            }
        }
        return res;
    }
}

leetcode 103. 二叉树的锯齿形层序遍历

题目描述:

在这里插入图片描述

思路:
同上题一致,再加上一个标志位来表示本层是否需要倒叙。达成锯齿遍历的目的。

代码:

 import java.util.*;

class Solution {
    
    
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    
    
List<List<Integer>> res = new ArrayList<List<Integer>>(); 
        List<Integer> tempNums = new ArrayList<Integer>();
        if(root == null) return res;
        Queue<TreeNode> temp = new LinkedList<TreeNode>();
        Queue<TreeNode> next = new LinkedList<TreeNode>();
        temp.offer(root);
        tempNums.add(root.val);
        res.add(new ArrayList<Integer>(tempNums));
        tempNums.clear();
        boolean flag = true;
        while(!temp.isEmpty()) {
    
    
            TreeNode tempNode = temp.poll();
            if (tempNode.left != null) {
    
    
                next.add(tempNode.left);
                tempNums.add(tempNode.left.val);
            }
            if (tempNode.right != null) {
    
    
                next.add(tempNode.right);
                tempNums.add(tempNode.right.val);
            }
            if (temp.isEmpty()) {
    
    
                if(tempNums.size() > 0){
    
    
                if(flag) {
    
    
                    Collections.reverse(tempNums);
                }
                flag = !flag;
                res.add(new ArrayList<Integer>(tempNums));
                tempNums.clear();
                temp = next;
                next = new LinkedList<TreeNode>();
                }
            }
        }
        return res;
    }
}

leetcode 121. 买卖股票的最佳时机

题目描述:
在这里插入图片描述
思路:
1、由于只是考虑一次购入以及售出的最大收益,可以维护一个最小值点,若比这个最小值点大,则计算收益,若收益大于max值,更新max值,最后返回max值。

代码:

class Solution {
    
    
    public int maxProfit (int[] prices) {
    
    
        int max = 0;
        int min = prices[0];
        for (int i = 1; i < prices.length; i ++) {
    
    
            if (prices[i] < min) {
    
    
                min = prices[i];
            } else {
    
    
                max = prices[i] - min > max ? prices[i] - min: max; 
            }
        }               
        return max;
    }
}

2、采用动态规划的思想,由于每个点的状态只有两种可能:持有股票、不持有股票,其中持有股票的情况只有可能来自本次购买股票,不持有股票的情况可能是本次卖出或者一直不持有股票,定义一个dp[2][j], 其中dp[0]表示不持有股票的最大值,dp[1]表示持有股票的最大值。其动态规划方程有:
dp[1][i] = dp[1][i - 1] < prices[i] ? dp[1][i - 1] : prices[i];
dp[0][i] = prices[i] - dp[1][i - 1] > dp[0][i - 1] ? prices[i] - dp[1][i - 1] : dp[0][i - 1];

代码:

class Solution {
    
    
    public int maxProfit (int[] prices) {
    
    
        int dp[][] = new int[2][prices.length];
        dp[1][0] = prices[0];
        dp[0][0] = 0;
        for (int i = 1; i < prices.length; i ++) {
    
    
            dp[1][i] = dp[1][i - 1] < prices[i] ? dp[1][i - 1] : prices[i];
            dp[0][i] = prices[i] - dp[1][i - 1] > dp[0][i - 1] ? prices[i] - dp[1][i - 1] : dp[0][i - 1];		//注意是prices[i] - dp[1][i - 1];
        }               
        return dp[0][prices.length - 1];
    }
}

leetcode 20. 有效的括号

题目描述:
在这里插入图片描述
思路:
用栈来做,其中若遇到左括号,弹入对应的右括号,若遇到右括号,判断栈顶的右括号与其是否匹配,若不匹配返回false,运行到最后,栈为空,则返回true。

import java.util.*;
class Solution {
    
    
    public boolean isValid(String s) {
    
    
        char strs [] = s.toCharArray();
        if (s.length() == 0 || s.length() % 2 == 1)    return false;
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < strs.length; i ++) {
    
    
            if (strs[i] == '{')  stack.push('}');
            else if (strs[i] == '(')  stack.push(')');
            else if (strs[i] == '[')  stack.push(']');
            else {
    
    
                if (stack.isEmpty() || stack.pop() != strs[i]) {
    
    
                    return false;
                }
            }
        }
        return stack.isEmpty() == true ? true : false;
    }
}

leetcode 88. 合并两个有序数组

题目描述:
在这里插入图片描述
思路:
1、可以通过正常思路,从后往前进行遍历。代码略。
2、可以通过从后往前遍历,想把最大的放到最后,依次往回遍历,若出现某个数组已经遍历完,把剩下的部分放到数组当中。

代码:

class Solution {
    
    
    public void merge(int[] nums1, int m, int[] nums2, int n) {
    
    
        int maxLength = m + n - 1;
        int l1 = m - 1;
        int l2 = n - 1;
        int temp = 0;
        while (l1 >= 0 || l2 >= 0) {
    
    
        //不一定是直接处理,可以先找到对应的数字再处理
            if (l1 == -1) temp = nums2[l2 --];
            else if (l2 == -1) temp = nums1[l1 --]; 
            else if (nums1[l1] > nums2[l2]) temp = nums1[l1 --];
            else if (nums1[l1] <= nums2[l2]) temp = nums2[l2 --];
            nums1[maxLength --] = temp;
        }
        }
    }

leetcode 415. 字符串相加

题目描述:

思路:
做大数加法的基本思路,模拟计算加法。

代码:

class Solution {
    
    
    public String addStrings(String num1, String num2) {
    
    
        int i = num1.length() - 1;
        int j = num2.length() - 1;
        int carry = 0;
        StringBuilder sb = new StringBuilder();
        while (i >= 0 || j >= 0 || carry > 0) {
    
    		// 若三者都大于零,说明还需要计算
            int x = i >= 0 ? num1.charAt(i) - '0' : 0;		//若有小于零的,该值设为0
            int y = j >= 0 ? num2.charAt(j) - '0' : 0;
            int res = x + y + carry;				//计算结果
            sb.append(res % 10);        //这里可以直接添加整形
            carry = res / 10;			//计算进位
            i --;						//二个同时--
            j --;
        }
        return sb.reverse().toString();
    }
}

leetcoed 236. 二叉树的最近公共祖先

题目描述:
在这里插入图片描述
思路:
采用前序遍历递归的方式来做这道题。
对于一个节点是双方节点的父节点有以下情况:

1、该节点是双方的祖先节点。
2、对于异侧的情况,该节点的左节点或者右节点有一个不是双方的祖先节点。
3、 对于同侧的情况,该节点的右节点或者左节点是双方的祖先节点。

其递归方程:
1、判断此节点是否为空或者等于节点1或者节点2,若是返回。
2、对左节点、右节点进行遍历
3、根据上述2、3点,若左节点为空,则说明右节点是其祖先节点,若左节点为空,则说明右节点是双方祖先节点。若双方都不为空,说明自己就是该最近祖先节点。
4、若双方都为空,说明不存在该节点。

代码:

class Solution {
    
    
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    
    
        if (root == null || root == p || root == q)  return root;    //终止条件。若root为空或者等于目标之一,则返回
        TreeNode left = lowestCommonAncestor(root.left, p, q);          //开始往左遍历
        TreeNode right = lowestCommonAncestor(root.right, p, q);         //开始往右遍历
        //if (left == null && right == null)  return null;              //其实这种情况一般不存在
        if (left == null)   return right;
        if (right == null)  return left;
        return root;
    }
}

leetcode 46. 全排列

题目描述:
在这里插入图片描述

思路:

先将所有的数字用一个list存起来,然后再进行回溯,定下第1个数、第2个…第N个,直到把所有的数字情况都遍历到。
走到最后之后开始回溯,跟dfs差不多的思想。

代码:

class Solution {
    
    
    public List<List<Integer>> permute(int[] nums) {
    
    
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        ArrayList<Integer> temp = new ArrayList<>();
        for (int i : nums) {
    
    
            temp.add(i);
        }
        if (nums.length ==0) {
    
    	
            return res;
        }
        if (nums.length == 1) {
    
    
            res.add(temp);
            return res;
        }
        permuteNum(res, temp, nums, 0);
        return res;
    }
    public void permuteNum(List<List<Integer>> res, List<Integer> temp, int [] nums, int i) {
    
    
        if (i == nums.length - 1) {
    
    					//如果走到最后,增加到结果集,返回
            res.add(new ArrayList<Integer>(temp));
        }
        for (int k = i; k < nums.length; k ++) {
    
    	//这里体现的思想就是定1个,其他的挨个交换遍历
            Collections.swap(temp, i, k);
            permuteNum(res, temp, nums, i + 1);     //定的是i + 1
            Collections.swap(temp, i, k);
        }
    }
}

leetcode 33. 搜索旋转排序数组

题目描述:
在这里插入图片描述
思路:
该数组是一个变形后的有序数组,采用二分查找的思想来做,因为本质上查找时,target只有两种可能,在左边、在右边,只要将这两种情况判定做好就好了。

主要分两种情况来做:
第1种就是nums[0]到mid之间有部分已经被反转的,此时mid的右侧数字大小只有可能小于nums[0],因此只要满足这个条件,则target在右侧,否则在左侧。

第2种就是nums[0]到mid之间是有序的,此时,若target大于nums[mid] 或者 target < nums[0],说明target在mid右侧,否则在左侧。

代码:

class Solution {
    
    
    public static int search(int[] nums, int target) {
    
    
        //边界条件处理
        if (nums.length == 0) {
    
    
            return -1;
        }
        if (nums.length == 1) {
    
    
            return nums[0] == target ? 0 : -1;
        }
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
    
    
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
    
    
                return mid;
            }
            if (nums[0] > nums[mid]) {
    
    				// 若nums[0] > nums[mid]的情况
                if (nums[mid] < target && target < nums[0]) {
    
    	// 若数字大于nums[mid] 且小于nums[0] ,target在右侧
                    left = mid + 1;
                } else {
    
    
                    right = mid - 1;
                }
            } else {
    
    							// 第二种情况
                if (nums[0] > target || target > nums[mid]) {
    
    	//若数字大于nums[mid] 或者小于nums[0]	,数字在右侧
                    left = mid + 1;
                } else {
    
    
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
}

leetcode 142. 环形链表 II

题目描述:
在这里插入图片描述

思路:
1、采用HashMap来存储,若出现重复节点,则说明该节点是入口节点,输出。
代码略。

2、采用快慢指针,二者从头节点开始,快指针每次走2步,慢指针每次走1步。若二者相遇,说明有环,若二者未相遇。说明无环。
若有环有这样子的关系:
快指针走过的路路径长度比慢指针刚好多环的长度,此时设置A 为快指针走过的长度,B为慢指针走过的长度。N为圈的长度,L为到圈的长度。
则有: A = 2B
且 A - B = KN (N为圈的长度)
且有L + KN = 入口节点。
此时若让A回到head节点再重新走L步,此时B也走L步,二者同时能够到达入口节点。

public class Solution {
    
    
    public ListNode detectCycle(ListNode head) {
    
    
        if (head == null)   return null;
        ListNode slow = head;
        ListNode fast = slow;
        while (fast != null) {
    
    
            slow = slow.next;
            if (fast.next != null) {
    
    
                fast = fast.next.next;
            } else {
    
    
                return null;
            }
            if (slow == fast) {
    
    
                fast = head;
                while (slow != fast) {
    
    
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;
            }
        }
        return null;
    }
}

leetcode 200. 岛屿数量

题目描述:
在这里插入图片描述
思路:
经典洪范法思路,若出现岛屿,将岛屿设置为海,然后再继续往上、下、左、右4个方向遍历。直到最后所有的岛屿都变成海。

代码:

class Solution {
    
    
    public int numIslands(char[][] grid) {
    
    
        int res = 0;
        for (int i = 0;i < grid.length; i ++) {
    
    
            for(int j = 0; j < grid[0].length; j ++) {
    
    
                if (grid[i][j] == '1') {
    
    		//若遇到岛屿,进行洪范法
                    ++ res;
                    hongfangfa(grid, i, j);
                }
            }
        }
        return res;
    }
    public void hongfangfa(char [][]chs, int i, int j) {
    
    
        if (i < 0 || j < 0 || i == chs.length || j == chs[0].length || chs[i][j] == '0') {
    
    	//先进行边界判断,若数组越界或者已经是海,返回上一层
            return ;
        }
        chs[i][j] = '0';			//岛屿设置成海
        hongfangfa(chs, i + 1, j);		//上、下、左右遍历
        hongfangfa(chs, i - 1, j);
        hongfangfa(chs, i, j + 1);
        hongfangfa(chs, i, j - 1);
    }
}

leetcode 5. 最长回文子串

题目描述:
在这里插入图片描述
思路:
1、采用中心扩散法,每个节点都发起一个中心扩散,判断其回文串长度,再根据长度确定下标,最后substring返回。

代码:

class Solution {
    
    
    public String longestPalindrome(String s) {
    
    
        int left = 0;
        int right = 0;
        for (int i = 0; i < s.length(); i ++) {
    
    
            int len1 = Palindrome(s.toCharArray(), i, i + 1);
            int len2 = Palindrome(s.toCharArray(), i, i);
            int tempLen = len1 > len2 ? len1 : len2;
            if (tempLen > right - left) {
    
           //每次长度都是变化的
                left = i - (tempLen - 1) / 2;   //这里i - (tempLen - 1) / 2是做偶数 - 1的操作,因为是偶数时i向右多遍历了一个点 
                right = i + tempLen / 2;
            }   
        }
        return s.substring(left, right + 1);
    }
    public int Palindrome(char []chs, int i, int j) {
    
    
        while (i >= 0 && j < chs.length && chs[i] == chs[j]) {
    
    
            i --;
            j ++;
        }
        return j - i - 1;
    }
}

leetcode 23. 合并K个升序链表

题目描述:
在这里插入图片描述

思路:
在合并二链表的基础上加上for循环,遍历所有链表并且合并返回。

代码:

class Solution {
    
    
    public ListNode mergeKLists(ListNode[] lists) {
    
    
        if (lists.length == 0) {
    
    
            return null;
        }
        if (lists.length == 1) {
    
    
            return lists[0];
        }
        ListNode res = null;
        for (int i = 0; i < lists.length; i ++) {
    
    
            res = mergeTwo(res, lists[i]);
        }
        return res;
    }
    public ListNode mergeTwo(ListNode l1, ListNode l2) {
    
    
        if (l1 == null) {
    
    
            return l2;
        }
        if (l2 == null) {
    
    
            return l1;
        }
        ListNode head = new ListNode(0);
        ListNode res = head;
        while(l1 != null && l2 != null) {
    
    
            if (l1.val <= l2.val) {
    
    
                head.next = l1;
                l1 = l1.next;
            } else {
    
    
                head.next = l2;
                l2 = l2.next;
            }
            head = head.next;
        }
        head.next = l1 == null ? l2 : l1;
        return res.next;
    }
}

leetcode 54. 螺旋矩阵

题目描述:
在这里插入图片描述
思路:
1、采用基本的遍历思路,按照边界去遍历,定义4条边界,每次再遍历完边之后缩小边界,最后遍历完成。

代码:

class Solution {
    
    
    public List<Integer> spiralOrder(int[][] matrix) {
    
    
        List<Integer> res = new ArrayList();
        int l = 0;							//定义左边界
        int r = matrix[0].length - 1;		//定义右边界
        int top = 0;						//定义上边界
        int buttom = matrix.length - 1;		//定义下边界
        while(true) {
    
    
            for (int i = l; i <= r; i ++) {
    
    	//遍历上边界
                res.add(matrix[top][i]);
            }
            if (++ top > buttom) {
    
    			//若边界缩小后出现重合,遍历结束
                break;
            }
            for (int i = top; i <= buttom; i ++) {
    
    
                res.add(matrix[i][r]);
            }
            if (-- r < l) {
    
    
                break;
            }
            for (int i = r; i >= l; i --) {
    
    
                res.add(matrix[buttom][i]);
            }
            if (-- buttom < top) {
    
    
                break;
            }
            for (int i = buttom; i >= top; i --) {
    
    
                res.add(matrix[i][l]);
            }
            if (++ l > r) {
    
    
                break;
            }
        }
        return res;
    }
}

leetcode 300. 最长递增子序列

题目描述:
在这里插入图片描述
思路:
1、采用动态规划的思想,维护一个到该节点最大连续序列长度的数组,进行一次双重循环,每次若出现前小于后的情况,根据情况更新dp[I];

代码:

class Solution {
    
    
    public int lengthOfLIS(int[] nums) {
    
    
        int []dp = new int[nums.length];
        dp[0] = 1;
        int res = dp[0];
        for (int i = 1; i < nums.length; i ++) {
    
    
            dp[i] = 1;
            for (int j = 0; j < i; j ++) {
    
    
                if (nums[i] > nums[j]) {
    
    
                    dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1;
                }
                res = dp[i] > res ? dp[i] : res;
            }
        }
        return res;
    }
}

2、思路1的优化版,维护左边最大高度数组和右边最大高度数据,再进行一次遍历,遍历完后就可以获得水多少。

 import java.util.*;
class Solution {
    
    
    public int trap(int[] height) {
    
    
        int res = 0;
        int [] leftMax = new int[height.length];
        int [] rightMax = new int[height.length];	
        leftMax[0] = height[0];						//要先处理两端边界
        rightMax[height.length - 1] = height[height.length - 1];	
        for (int i = 1;i < height.length; i ++) {
    
    
            leftMax[i] = Math.max(leftMax[i-1], height[i]);
        }
        for (int i = height.length - 2; i >= 0; i --) {
    
    
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }
        for (int i = 1; i < height.length - 1; i ++) {
    
    
            if (Math.min(leftMax[i - 1], rightMax[i + 1]) > height[i]) {
    
    
                res += Math.min(leftMax[i - 1], rightMax[i + 1]) - height[i];
            }
        }
        return res;
    }
}

leetcode 143. 重排链表

题目描述:
在这里插入图片描述
思路:
采用比较直接的思路,先获取链表中点,获取到之后将链表分为两段,后面那段进行逆转。
然后合并两个链表。

代码:

class Solution {
    
    
    public void reorderList(ListNode head) {
    
    
        ListNode slow = new ListNode(0);
        slow.next = head;
        ListNode fast = head;
        while (fast != null) {
    
    
            slow = slow.next;
            fast = fast.next;
            if (fast == null) {
    
    
                break;
            }
            fast = fast.next;
        }
        ListNode newHead = reverse(slow.next);
        ListNode temp = head;
        slow.next = null;
        while (temp != null && newHead != null) {
    
    
            ListNode next = temp.next;
            ListNode newHeadNext = newHead.next;
            temp.next = newHead;
            temp = next;
            newHead.next = temp;
            newHead = newHeadNext;
        }
    }
    public ListNode reverse(ListNode head) {
    
    
        ListNode res = null;
        while (head != null) {
    
    
            ListNode next = head.next;
            head.next = res;
            res = head;
            head = next;
        }
        return res;
    }
}

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

题目描述:
在这里插入图片描述
思路:
本质上跟找倒数第K个节点差不多,采用快慢指针来做,快指针先走K步,走完之后慢指针、快指针同时走,遇到快指针走到末尾后,将慢指针下一个节删除。结束。

代码:

class Solution {
    
    
    public ListNode removeNthFromEnd(ListNode head, int n) {
    
    
        ListNode slow = new ListNode(0);
        slow.next = head;
        ListNode res = slow;
        while (n -- > 0) {
    
    
            fast = fast.next;
        }
        while (fast != null) {
    
    
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;				// 这里就是删除下一个节点
        return head;
    }
}

leetcode 2. 两数相加

题目描述:
在这里插入图片描述
思路:
按照大数加法的思路来做,只不过用了一个节点来存储每一位数字,最后再进行返回。

代码:

class Solution {
    
    
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    
    
        ListNode temp = new ListNode(0);
        ListNode res = temp;
        int jin = 0;
        while(l1 != null || l2 != null || jin != 0) {
    
    
            int num1 = l1 == null ? 0 : l1.val;
            int num2 = l2 == null ? 0 : l2.val;
            int sum = num1 + num2 + jin;
            temp.next = new ListNode(sum % 10);		// 用节点来存储数字
            jin = sum / 10;
            if (l1 != null) {
    
    
                l1 = l1.next;
            }
            if (l2 != null) {
    
    
                l2 = l2.next;
            }
            temp = temp.next;
        }
        return res.next;
    }
}

leetcode 82. 删除排序链表中的重复元素 II

题目描述:
在这里插入图片描述
思路:
这道题用迭代的方式来做,对于细节的处理会有很多方法,自己使用的是用一个pre节点记录上一节点,在重复的时候就把节点移动到重复的最后一个节点,再pre.next = temp.next达到删除重复节点的目的。
如果发现无重复再更新pre。

代码:

class Solution {
    
    
    public ListNode deleteDuplicates(ListNode head) {
    
    
        ListNode temp = head;
        ListNode pre = new ListNode(-1, head);			//用一个空节点来防止出现首节点重复问题
        ListNode res = pre;
        while (temp != null) {
    
    
            if (temp.next != null && temp.next.val == temp.val) {
    
    
                while (temp.next != null && temp.next.val == temp.val) {
    
    
                    temp = temp.next;
                }
                pre.next = temp.next;
            } else {
    
    
                pre = temp;
            }
            temp = temp.next;
        }
        return res.next;
    }
}

leetcode 93. 复原 IP 地址

题目描述:
在这里插入图片描述
思路:
采用回溯的方法来做,回溯遍历所有可能的情况,再进行剪枝操作。
本题的难点主要在于代码的编写。

代码:

class Solution {
    
    
    public List<String> restoreIpAddresses(String s) {
    
    
        Stack<String> stack = new Stack();					//用于暂存遍历情况
        ArrayList<String> res = new ArrayList<String>();	//用于存储返回结果
        int len = s.length();
        if (len < 4 || len > 12) {
    
    
            return res;
        }
        dfs(s, 0, 0, len, stack, res);
        return res;
    }
    public void dfs(String s, int left, int num, int len, Stack<String> stack, ArrayList<String> res) {
    
    
        if (len == left) {
    
    			//若已经遍历到字符串最后位置,开始判断是否已经遍历完成,若完成则将结果加入,然后开始回溯
            if (num == 4) {
    
    
                res.add(String.join(".", stack)); 		// 采用Sting.join函数,可以在List<String>中间插入"."
            }
            return;
        }
        if (len - left < (4 - num) || len - left > 3 * (4 - num)) {
    
    		//若当前出现无法遍历完成的情况,剩余长度太长或者太短,则剪枝回溯
            return;
        }
        for (int i = 0; i < 3; i ++) {
    
    				//遍历所有情况
            if (left + i >= len) {
    
    
                break;
            }
            int str = judge(s, left, left + i);
            if (str != -1) {
    
    
                stack.push(String.valueOf(str));		// 若判断可用,将String临时加入到stack中
                dfs(s, left + i + 1, num + 1, len, stack, res);
                stack.pop();							//遍历结束后出栈
            }
        }
    }
    public int judge(String s, int left, int right) {
    
    			//判断函数
        int len = right - left + 1;
        if (len > 1 && s.charAt(left) == '0') {
    
    
            return -1;
        }
        int res = 0;
        for(; left <= right; left ++) {
    
    
            res = res * 10 + s.charAt(left) - '0';
        }
        if (res > 255) {
    
    
            return -1;
        }
        return res;
    }
}

leetcode 148.排序链表

题目描述:
在这里插入图片描述

思路:
1、通过归并排序的思想来做,先将所有的链表按照中间节点分割为两部分,直到分到不可再分后就开始合并两个子链表,直到所有的链表合成完毕。

代码:

class Solution {
    
    
    public ListNode sortList(ListNode head) {
    
    
    	// 头节点为空或者只有一个节点时,开始回溯操作,也就是开始并的操作
        if (head == null || head.next == null) {
    
    
            return head;
        }
        // 先获得中间节点
        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) {
    
    
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode temp = slow.next;
        // 打断中间节点的联系
        slow.next = null;
        // 开始分的操作
        ListNode left = sortList(head);
        ListNode right = sortList(temp);
        ListNode res = new ListNode(0);
        ListNode resHead = res;
        // 合并两个有序链表的方法来执行
        while (left != null && right != null) {
    
    
            if (left.val < right.val) {
    
    
                res.next = left;
                left = left.next;
            } else {
    
    
                res.next = right;
                right = right.next;
            }
            res = res.next;
        }
        res.next = left == null ? right : left;
        // 最后将此段返回
        return resHead.next;
    }
}

leetcode 151.翻转字符串里的单词

题目描述:
在这里插入图片描述

思路:
1、用trim去除双端空格
2、用split()方法按照空格隔开
3、用栈把单词压入
4、组成结果字符粗
代码:

class Solution {
    
    
    public String reverseWords(String s) {
    
    
        s = s.trim();
        String strs[] = s.split(" ");
        if (strs.length == 0) {
    
    
            return "";
        }
        if (strs.length == 1) {
    
    
            return strs[0];
        }
        Stack<String> stack = new Stack<String>();
        for(String str : strs) {
    
    
            stack.push(str);
        }
        StringBuilder sb = new StringBuilder();
        while(stack.size() > 1) {
    
    
            String temp = stack.pop();
            // 若遇到空格跳过
            if (temp.length() > 0) {
    
    
                sb.append(temp);
                sb.append(" ");
            }
        }
        sb.append(stack.pop());
        return sb.toString();
    }
}

猜你喜欢

转载自blog.csdn.net/Nimrod__/article/details/119846017