LeetCode 剑指 刷题总结
Leetcode 69 求 X 的平方根(保留整数):二分法;O(logX)
二分查找:给出左右边界,l=0;r=x;ans=-1;int mid=l+(r-l)/2
mid*mid与x的大小;若mid x mid <=x,则 l=mid+1,并将mid赋值给ans;否则 r=mid-1;
class Solution {
public int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long) mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
}
leetcode 7 整数反转:除以10,再取模;O(log|x|)
除以10,再取模
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {
return 0;
}
int digit = x % 10;
x /= 10;
rev = rev * 10 + digit;
}
return rev;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/reverse-integer/solution/zheng-shu-fan-zhuan-by-leetcode-solution-bccn/
剑指offer 19顺时针打印矩阵:模拟;O(M*N)
可以模拟打印矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。(leetcode官方)
class Solution {
public int[] spiralOrder(int[][] matrix) {
//判断二维数组是否为空
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return new int[0];
}
int rows = matrix.length, columns = matrix[0].length;//获得二维数组的总行数和总列数
boolean[][] visited = new boolean[rows][columns];//定义一个二维数组,用来存放已经遍历过的位置;
int total = rows * columns;//获得总的元素数;
int[] order = new int[total];//定义一个新的大小为total的数组,用来存放遍历过的数组,用作最后的输出;
int row = 0, column = 0;
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//定义方向数组
int directionIndex = 0;//定义方向索引;
for (int i = 0; i < total; i++) {//开始遍历二维数组;
order[i] = matrix[row][column];
visited[row][column] = true;
int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
directionIndex = (directionIndex + 1) % 4;//如果超过了本行或者是本列,则改变方向;
}
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
}
}
字符串
leetcode-cn20有效括号 O(N)
栈:判断括号的有效性可以使用「栈」这一数据结构来解决。
我们遍历给定的字符串 s。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。
当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 s 无效,返回 False。为了快速判断括号的类型,我们可以使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。
class Solution {
public boolean isValid(String s) {
int n=s.length();
if(n%2==1){
return false;
}
Map<Character,Character> map= new HashMap<Character,Character>();
map.put(')','(');
map.put(']','[');
map.put('}','{');
Deque<Character> stack = new LinkedList<>();
for(int i=0;i<n;i++){
char ch=s.charAt(i);
if(map.containsKey(ch)){
if(stack.isEmpty() || stack.peek()!=map.get(ch)){
return false;
}
stack.pop();
}else{
stack.push(ch);
}
}
return stack.isEmpty();
}
}
leetcode 3无重复字符串的最长子串O(N)
双指针+哈希表
class Solution {
public int lengthOfLongestSubstring(String s) {
int start =0;
int end=0;
int n=s.length();
Map<Character,Integer> map = new HashMap<>();
for(int i=0;i<n;i++){
char c = s.charAt(i);
if(map.containsKey(c) ){
start=Math.max(map.get(c),start);
}
map.put(c,i+1);
end=Math.max(end,i-start+1);
}
return end;
}
}
剑指48 最长不含重复字符串的子字符串 O(N)
双指针+哈希表
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> dic = new HashMap<>();
int i = -1, res = 0;
for(int j = 0; j < s.length(); j++) {
if(dic.containsKey(s.charAt(j)))
i = Math.max(i, dic.get(s.charAt(j))); // 更新左指针 i
dic.put(s.charAt(j), j); // 哈希表记录
res = Math.max(res, j - i); // 更新结果
}
return res;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/solution/mian-shi-ti-48-zui-chang-bu-han-zhong-fu-zi-fu-d-9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(困难)leetcode 32 最长有效括号
(1)动态规划:O(N)
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
int[] dp = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = Math.max(maxans, dp[i]);
}
}
return maxans;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
(2)栈:O(N)
始终保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标:
对于遇到的每个 ‘(’ ,我们将它的下标放入栈中
对于遇到的每个 ‘)’ ,我们先弹出栈顶元素表示匹配了当前右括号:
如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
如果栈不为空,当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Deque<Integer> stack = new LinkedList<Integer>();
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(i);
} else {
stack.pop();
if (stack.empty()) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
(中等)leetcode 5最长回文子串O(N x N)
动态规划:对于一个子串而言,如果它是回文串,并且长度大于 22,那么将它首尾的两个字母去除之后,它仍然是个回文串。
public class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
boolean[][] dp = new boolean[len][len];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= len; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= len) {
break;
}
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
剑指34 字符串中第一个只出现一次的字符:哈希表;O(n)
使用哈希表存储频数
class Solution {
public char firstUniqChar(String s) {
Map<Character, Integer> frequency = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);
}//getOrDefault(key,defaultvalue) 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值defaultvalue.
for (int i = 0; i < s.length(); ++i) {
if (frequency.get(s.charAt(i)) == 1) {
return s.charAt(i);
}
}
return ' ';
}
}
数组:
买卖股票:
方法一:暴力;O(nn)
public class Solution {
public int maxProfit(int prices[]) {
int maxprofit = 0;
for (int i = 0; i < prices.length - 1; i++) {
for (int j = i + 1; j < prices.length; j++) {
int profit = prices[j] - prices[i];
if (profit > maxprofit) {
maxprofit = profit;
}
}
}
return maxprofit;
}
}
方法二:一次遍历;O(n)
public class Solution {
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
}
剑指27 斐波那契数组:动态规划 O(N)
class Solution {
public int fib(int n) {
int a = 0, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/solution/mian-shi-ti-10-i-fei-bo-na-qi-shu-lie-dong-tai-gui/
剑指 28数组中出现次数超过一半的数字:哈希表:O(N)
class Solution {
public int majorityElement(int[] nums) {
Map<Integer,Integer> ant = new HashMap<>();
for(int num : nums){
ant.put(num, ant.getOrDefault(num, 0) + 1);
if(ant.get(num) > nums.length / 2) return num;
}
return 0;
}
}
作者:rain-ru
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/solution/su-kan-duo-chong-fang-fa-jian-dan-yi-don-vzpd/
leetcode 1两数之和:
(1)暴力法:O(n*n)
两次for循环遍历:
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
return new int[]{i,j};
}
}
}
return new int[0];
}
}
(2)哈希表:O(N),将x和target-X存在hashtable中,一个放key,另一个则是value;
创建一个哈希表,对于每一个 x
,我们首先查询哈希表中是否存在 target - x
,然后将 x
插入到哈希表中,即可保证不会让 x
和自己匹配。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[0];
}
}
leetcode 136 数组中只出现一次的元素:异或 O(N) 符号:^
(1)任何数和 0 做异或运算,结果仍然是原来的数,
(2)任何数和其自身做异或运算,结果是 0
(3)异或运算满足交换律和结合律,
因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。
class Solution {
public int singleNumber(int[] nums) {
int single =0;
for(int num:nums){
single ^=num;
}
return single;
}
}
leetcode 38,在排序数组中,找出指定元素的第一个位置和最后一个位置;
二分查找:O(N)
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftIdx = binarySearch(nums, target, true);
int rightIdx = binarySearch(nums, target, false) - 1;
if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
return new int[]{leftIdx, rightIdx};
}
return new int[]{-1, -1};
}
public int binarySearch(int[] nums, int target, boolean lower) {
int left = 0, right = nums.length - 1, ans = nums.length;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > target || (lower && nums[mid] >= target)) {
right = mid - 1;
ans = mid;
} else {
left = mid + 1;
}
}
return ans;
}
}
leetcode 56合并区间:
class Solution {
public int[][] merge(int[][] intervals) {
List<int[]> res = new ArrayList<>();
//不排序,直接遍历列表
for(int[] i:intervals){
for(int j =0;j<res.size();j++){
int[] re = res.get(j);
if(re[0]<=i[1]&&re[0]>=i[0]){
i[1]=re[1]>i[1]?re[1]:i[1];
}else if(re[1]>=i[0]&&re[1]<=i[1]){
i[0]=re[0];
}else if(re[0]<i[0]&&re[1]>i[1]){
i=re;
}else{
continue;
}
res.remove(re);
j--;
}
res.add(i);
}
return res.toArray(new int[0][]);
}
}
解题链接:https://leetcode-cn.com/problems/merge-intervals/comments/815437
leetcode 64 最小路径和:O(MN)
动态规划
leetcode 287 寻找数组中的重复数(数组只存在唯一一个重复数O(N)
采用链表有环的思想
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
leetcode 448 寻找N个整数数组中缺失的元素(无序数组1-N,但是缺失中间某几个) O(N)
采用hash表;我们可以用一个哈希表记录数组 nums 中的数字,由于数字范围均在 1-N中,记录数字后我们再利用哈希表检查 1-N中的每一个数是否出现,从而找到缺失的数字,本方法用arraylist代替hash表,采用下标标记法
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
int n = nums.length;
// 遍历数组,把值对应的下标加n,后续数组大于n的下标表示存在的值
for (int num : nums) {
// 计算出下标,因为是取余,所以就算这个位置之前有加过n,取余之后也不会影响原来的值,
int x = (num - 1) % n;
// 下标对应的值+1
nums[x] += n;
}
// 存放结果数组
List<Integer> ret = new ArrayList<Integer>();
// 遍历数组,把没有出现过的数组添加到结果数组中
for (int i = 0; i < n; i++) {
// 因为存在的值都会加在对应数组下标上面加上n,所以肯定大于n
if (nums[i] <= n) {
ret.add(i + 1);
}
}
return ret;
}
}
leetcode 581 最短无序子数组 O(NlogN)
给你一个整数数组 nums
,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
排序,然后对比排序前后的数组
public class Solution {
public int findUnsortedSubarray(int[] nums) {
int[] snums = nums.clone();
Arrays.sort(snums);
int start = snums.length, end = 0;
for (int i = 0; i < snums.length; i++) {
if (snums[i] != nums[i]) {
start = Math.min(start, i);
end = Math.max(end, i);
}
}
return (end - start >= 0 ? end - start + 1 : 0);
}
}
(困难)leetcdoe 15三数之和 :排序+双指针;O(N*N)
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组.
a从最左边开始,c从最右边开始,b从a+1开始;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}
排序算法:
冒泡排序:稳定!时间复杂度n*n
比较相邻元素的大小:第一趟,最后位置得出是最大的元素;第二趟,倒数第二个位置的是倒数第二大的元素……共需要n-1趟,n代表所有元素的个数;
public class BubbleSort(){
public static void main(String args[]){
}
public static void bubbleSort(int[] arr){
int temp=0;
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
}
插入排序:稳定!时间复杂度:n*n
插入排序:将数组一份为2;一个A数组只有一个元素,剩下的B数组包含n-1个元素。每次从n-1个中,拿出一个,放到A中(并做比较)
public void doInsertSort(int[] arr){
for(int index = 1; index<arr.length; index++){//外层向右的index,即作为比较对象的数据的index
int temp = array[index];//用作比较的数据
int leftindex = index-1;
while(leftindex>=0 && array[leftindex]>temp){//当比到最左边或者遇到比temp小的数据时,结束循环
array[leftindex+1] = array[leftindex];
leftindex--;
}
array[leftindex+1] = temp;//把temp放到空位上
}
}
归并排序:分治思想。稳定,n*logn
public void mergeSort(int[] arr,int left,int right,int[] temp){
while(left<right){
int mid = (left+right)/2;//中间索引
mergeSort(arr,left,mid,temp);//向左递归分治
mergeSort(arr,mid+1,right,temp);//向右递归分治
merge(arr,left,mid,right,temp);//合并
}
}
public void merge(int[] arr,int left,int mid,int right,int[] temp){
int i=left;
int j=mid+1;
int t=0;
while(i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t]=arr[i];
t+=1;
i+=1;
}else{
temp[t]=arr[j];
t+=1;
j+=1;
}
}
//(二) 把剩余的数据,分两边填充到temp
while(i<=mid){
temp[t]=arr[i];
i+=1;
t+=1;
}
while(j<=right){
temp[t]=arr[j];
t+=1;
j+=1;
}
//(三) 将temp的数组拷贝到arr
t=0;
int templeft=left;
while(templeft<=right){
arr[templeft]=temp[t];
t+=1;
templeft+=1;
}
}
快速排序:不稳定,n*logn
public static void quickSort(int[] arr,int low,int high){
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
链表
leetcode 234 回文链表:双指针!
请判断一个链表是否为回文链表:
找到前半部分链表的尾节点(快慢指针);反转后半部分链表(链表的反转);判断是否回文;恢复链表;返回结果。
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}
// 找到前半部分链表的尾节点并反转后半部分链表
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHalfStart = reverseList(firstHalfEnd.next);
// 判断是否回文
ListNode p1 = head;
ListNode p2 = secondHalfStart;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) {
result = false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
}
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
leetcode160(剑指36) 相交链表(两个链表的第一个公共结点) O(m+n)
双指针
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
leetcode148 升序链表:
归并排序:递归法
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 tmp = slow.next;
slow.next = null;
ListNode left = sortList(head);
ListNode right = sortList(tmp);
ListNode h = new ListNode(0);
ListNode res = h;
while (left != null && right != null) {
if (left.val < right.val) {
h.next = left;
left = left.next;
} else {
h.next = right;
right = right.next;
}
h = h.next;
}
h.next = left != null ? left : right;
return res.next;
}
}
leetcode 141 判断链表中是否有环
快慢指针:O(N)
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
哈希表:O(N)
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<ListNode>();
while (head != null) {
if (!seen.add(head)) {
return true;
}
head = head.next;
}
return false;
}
}
leetcode 24 合并两个有序链表
递归:O(m+n)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
leetcode 19 删除链表的倒数第N个节点
双指针:O(L)
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
ListNode first = head;
ListNode second = dummy;
for (int i = 0; i < n; ++i) {
first = first.next;
}
while (first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
ListNode ans = dummy.next;
return ans;
}
}
(简单)剑指 3从尾到头打印链表:辅助栈 O(N)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/solution/mian-shi-ti-06-cong-wei-dao-tou-da-yin-lian-biao-b/
二叉树
leetcode 94中序遍历:O(N)
递归:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
preOrder(root,res);
return res;
}
public void preOrder(TreeNode root,List<Integer> res){
if(root==null){
return;
}
preOrder(root.left,res);
res.add(root.val);
preOrder(root.right,res);
}
}
(简单)剑指18 二叉树的镜像
方法一:递归法 O(N)
1、终止条件: 当节点 root为空时(即越过叶节点),则返回 null ;
2、递推工作:
初始化节点 tmp ,用于暂存 root 的左子节点;
开启递归 右子节点 mirrorTree(root.right) ,并将返回值作为 root 的 左子节点 。
开启递归 左子节点 mirrorTree(tmp) ,并将返回值作为 root 的 右子节点 。
3、返回值: 返回当前节点 root ;
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);
return root;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/solution/mian-shi-ti-27-er-cha-shu-de-jing-xiang-di-gui-fu-/
方法二:辅助栈O(N)
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
Stack<TreeNode> stack = new Stack<>() {{ add(root); }};
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
if(node.left != null) stack.add(node.left);
if(node.right != null) stack.add(node.right);
TreeNode tmp = node.left;
node.left = node.right;
node.right = tmp;
}
return root;
}
}
(简单)剑指 38 二叉树深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度
方法一:DFS:O(N)
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/solution/mian-shi-ti-55-i-er-cha-shu-de-shen-du-xian-xu-bia/
方法二:BFS(队列实现) 😮(N)
每遍历一层,则计数器 +1 ,直到遍历完成,则可得到树的深度
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
List<TreeNode> queue = new LinkedList<>();
queue.add(root);
List<TreeNode> tmp;
int res = 0;
while(!queue.isEmpty()) {
tmp = new LinkedList<>();
for(TreeNode node : queue) {
if(node.left != null) tmp.add(node.left);
if(node.right != null) tmp.add(node.right);
}
queue = tmp;
res++;
}
return res;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/solution/mian-shi-ti-55-i-er-cha-shu-de-shen-du-xian-xu-bia/
(中等)剑指 39平衡二叉树O(NlogN)
先序遍历+判断深度(自顶至底)
所有子树都需要满足平衡树性质!所以有三个条件:
1、abs(self.depth(root.left) - self.depth(root.right)) <= 1,判断当前子树是否平衡;
2、self.isBalanced(root.left),判断左子树是否平衡;
3、self.isBalanced(root.roght),判断右子树是否平衡。
class Solution{
public boolean isBalaced(TreeNode root){
if(root==null){
return true;
}
return Math.abs(Depth(root.left)-Depth(root.right))<=1 && isBalanced(root.left) && isBalanced(root.right);
}
//求最大深度
public int Depth(TreeNode root){
if(root == null){
return 0;
}
return Math.max(Depth(root.left),Depth(root.right))+1;
}
}
(简单)二叉树的第k小节点:O(N)
中序遍历(即得到二叉树的顺序排序)
class Solution {
int res, k;
public int kthLargest(TreeNode root, int k) {
this.k = k;
dfs(root);
return res;
}
void dfs(TreeNode root) {
if(root == null) return;
dfs(root.left);
if(k == 0) return;
if(--k == 0) res = root.val;
dfs(root.right);
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/solution/mian-shi-ti-54-er-cha-sou-suo-shu-de-di-k-da-jie-d/
栈
(简单)剑指09 两个栈实现队列 :
双栈(一个出,一个入(Dueue–>LinkedList()))时间O(1);空间O(n)
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
if(stack2.isEmpty()){
return -1;
}else{
int deleteNum=stack2.pop();
return deleteNum;
}
}
}
动态规划
leetcode 跳台阶:动态规划:O(N)
设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1级或 2级台阶。
1、当为 1级台阶: 剩 n-1 个台阶,此情况共有 f(n-1) 种跳法;
2、当为 2级台阶: 剩 n-2 个台阶,此情况共有 f(n-2)种跳法
f(n) 为以上两种情况之和,即 f(n)=f(n-1)+f(n-2),以上递推性质为斐波那契数列。本题可转化为 求斐波那契数列第 n 项的值 :初始值f(0)=1 , f(1)=1 , f(2)=2 ;
class Solution {
public int numWays(int n) {
int a = 1, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/solution/mian-shi-ti-10-ii-qing-wa-tiao-tai-jie-wen-ti-dong/
剑指 9 跳台阶 O(N)
青蛙可以跳1节台阶,2节台阶……n节台阶:
所以f(n)=f(n-1)+f(n-2)+……+f(2)+f(1);化简——>f(n)=2*f(n-1);
public class Solution {
public int jumpFloorII(int target) {
int f=1,fn=1;
for(int i=2;i<=target;i++){
fn=2*f;
f=fn;
}
return fn;
}
}
剑指30 连续子数组的最大和 动态规划 O(N)
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int res = array[0]; //记录当前所有子数组的和的最大值
int max=array[0]; //包含array[i]的连续数组最大值
for (int i = 1; i < array.length; i++) {
max=Math.max(max+array[i], array[i]);
res=Math.max(max, res);
}
return res;
}
}
模拟
扑克牌顺子:O(N)
class Solution {
public boolean isStraight(int[] nums) {
int joker = 0;
Arrays.sort(nums); // 数组排序
for(int i = 0; i < 4; i++) {
if(nums[i] == 0) joker++; // 统计大小王数量
else if(nums[i] == nums[i + 1]) return false; // 若有重复,提前返回 false
}
return nums[4] - nums[joker] < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
}
}