文章目录
- leetcode 206. 反转链表
- leetcode 3. 无重复字符的最长子串
- leetcode 146. LRU缓存机制
- leetcode 215. 数组中的第K个最大元素
- leetcode 25. K 个一组翻转链表
- leetcode 补充题4. 手撕快速排序
- leetcode 1. 两数之和
- leetcode 15. 三数之和
- leetcode 53. 最大子序和
- leetcode 21. 合并两个有序链表
- leetcode 141. 环形链表
- leetcode 160. 相交链表
- leetcode 102. 二叉树的层序遍历
- leetcode 103. 二叉树的锯齿形层序遍历
- leetcode 121. 买卖股票的最佳时机
- leetcode 20. 有效的括号
- leetcode 88. 合并两个有序数组
- leetcode 415. 字符串相加
- leetcoed 236. 二叉树的最近公共祖先
- leetcode 46. 全排列
- leetcode 33. 搜索旋转排序数组
- leetcode 142. 环形链表 II
- leetcode 200. 岛屿数量
- leetcode 5. 最长回文子串
- leetcode 23. 合并K个升序链表
- leetcode 54. 螺旋矩阵
- leetcode 300. 最长递增子序列
- leetcode 143. 重排链表
- leetcode 19. 删除链表的倒数第 N 个结点
- leetcode 2. 两数相加
- leetcode 82. 删除排序链表中的重复元素 II
- leetcode 93. 复原 IP 地址
- leetcode 148.排序链表
- leetcode 151.翻转字符串里的单词
本文为自己为秋招准备的刷题记录,题目列表来自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();
}
}