十、算法专题

165 阅读8分钟

一、简单题目合集

1、反转链表

题目: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

image.png

思路:

  • 设置一个前置节点pre以及当前需要反转的节点cur
  • 将当前需要反转的cur节点的指针next指向pre
  • 将已经反转的节点设置为pre,同时将cur继续后移,继续处理
  • 循环直到cur为空 代码:

class Solution {
    public ListNode reverseList(ListNode head) {
        //1、创建一个用于移动的节点
        ListNode pre = null;
        //2、记录当前节点(当前需要处理的节点,下面是每个节点单独处理的)
        ListNode current = head;
        while(current != null){
            //3、记录当前节点的下一个节点(因为当前节点处理完指针已经指向pre了,需要先存储下一个需要处理的节点)
            ListNode tmp = current.next;
            //4、修改当前节点的指针到前一个
            current.next = pre;
            //5、指针指向修改后,把自己作为目标节点让后面指向自己
            pre = current;
            //6、将当前需要处理的节点设置为下一个节点
            current = tmp;
        }
        //7、返回当前最后一个节点(已经更换为头节点)
        return pre;
    }
}

2、合并两个有序链表

题目: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

思路: 注意关键点:有序 如果是两个链表或者数组合并,第一时间要想到递归!!!

代码:

lass Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null){
            return list2;
        }
        if (list2 == null){
            return list1;
        }
        if (list1.val < list2.val){
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;
        }else{
            list2.next = mergeTwoLists(list1,list2.next);
            return list2;
        }
    }
}

3、最大子数组和

题目

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。


示例 :

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

思路

核心思路:动态规划

动态规划有5个步骤:

  • (1)设置一个dp[]的数组,思考当前i以及dp[i]的含义是什么
  • (2)找到可循环的规律,公式
  • (3)初始化数组:其实就是初始化一个或者两个规律上的初始值
  • (4)写循环
  • (5)打印dp[]数组,看下是否符合要求

根据动态规划的5个步骤,求最大的连续子数组的和可以这么思考:

-(1)dp[]数组中,i是当前的下标,dp[i]代表当前下标的最大和 -(2)这道题可以转换一个思路,类似有a b c 三个人,从a开始,a可能负债可能有钱,a找到b,b看了下a+b的钱还不如自己,那就自己重新开始往后找,b找到了c,发现b+c > b ,那么就保留b开始,同时计算最大的值是b+c

代码

class Solution {
     public static int maxSubArray(int[] nums) {
         //如果是空数组直接返回0
        if (nums == null || nums.length == 0){
            return 0;
        }
        //1、定义一个数组,数组的每个值是记录当前下标下最高的值
        int [] dp = new int[nums.length];
        //2、初始化第一个值为最大的结果
        dp[0] = nums[0];
        int result = nums[0];
        //3、进行比较,如果前面的和小于当前的值,则放弃前面的和重新开始
        for(int i = 1;i < nums.length;i++){
            dp[i] = Math.max(dp[i-1] + nums[i],nums[i]);
            result = Math.max(dp[i],result);
        }
        return result;
    }
}

4、二分查找

题目

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。


示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

思路

class Solution {
    public int search(int[] nums, int target) {
     if (nums == null || nums.length ==0){
         return -1;
     }
     int left = 0,right = nums.length -1;
     while (left <= right){
         //注意 核心在这里 需要注意中间的角标是怎么算出来的
         int middle = left + (right - left)/2;
         int compare = nums[middle];
         if (compare == target){
             return middle;
         }else if (compare > target){
             right = middle -1;
         }else {
             left = middle +1;
         }
     }
     return -1;
    }
}

5、234. 回文链表

题目

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false

思路

核心思路是利用栈的思想(回文都可以考虑下用栈或者反转链表的方式来做)

代码

class Solution {
    public boolean isPalindrome(ListNode head) {
      if (head == null){
          return false;
      }
      ListNode current = head;
      //先计算长度并且把数据入栈
      int length = 0;
      Stack<Integer> stack = new Stack();
      while(current != null){
          stack.push(current.val);
          current = current.next;
          length ++;
      }
      boolean flag = true;
      //数据出栈,对比数据是否完全一致,如果发现不一致,则不是回文
      while(!stack.isEmpty()){
          int value = stack.pop();
          int val = head.val;
          head = head.next;
          if(value != val){
              flag = false;
              break;
          }
      }
      return flag;

    }
}

6、 141. 环形链表

题目


如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

思路

判断是否有环的核心方式是快慢指针,如果有环,一个快一个慢,一定会相遇

代码

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

7、70. 爬楼梯

题目

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

思路

这也是找规律,根据大的拆成小的动态规划的题目

代码

class Solution {
    public int climbStairs(int n) {
       if (n <= 1){
           return 1;
       }
       int d1 = 1;
       int d2 = 1;
       for (int i = 2;i<= n;i++){
           int tmp = d2;
           d2 = d1+d2;
           d1 = tmp;
       }
       return d2;
    }
}

8、160. 相交链表

题目

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

思路

判断是否相交的核心思路是两个指针走过的路是一模一样的。 只要两个人走的路径加起来一样,速度一样,那么一定会在某个时间点相遇。

代码

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null){
            return null;
        }
        ListNode currentA = headA;
        ListNode currentB = headB;
        while(currentA != currentB){
            currentA = currentA == null?headB:currentA.next;
            currentB = currentB == null?headA:currentB.next;
        }
        return currentA;
    }
}

9、121. 买卖股票的最佳时机

题目

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。


示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

思路

如果看到一串数组,寻找最大值,最小值,连续最大和等关键字,毫无疑问用动态规划。

  • 分析当前价格与后面的价格开始的优势,如果没有优势,则用后面的重新开始。
  • 在内存中维护一个最大值

代码

class Solution {
    public int maxProfit(int[] prices) {
       if(prices == null || prices.length == 0){
           return 0;
       }
       //设置初始成本
       int currentCost = prices[0];
       //设置初始利润
       int currentMoney = 0;
       //设置最大利润
       int maxProfit = 0;
       for(int i = 1;i < prices.length;i++){
           //设置当前比较的成本差
           int compareCost = prices[i] - currentCost;
           //如果亏钱 还不如重新开始设置当前值为成本价
           if(compareCost <0){
              currentCost = prices[i];
           }
           maxProfit = Math.max(maxProfit,compareCost);
       }
       return maxProfit;
    }
}

9、剑指 Offer 22. 链表中倒数第k个节点

题目

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

 

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

思路 先计算长度,算出倒数第k个的位置是length -k,遍历就可以得到 代码

class Solution {
     public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return null;
        }
        //计算长度
        ListNode cur = head;
        int length = 0;
        while(cur != null){
            cur = cur.next;
            length ++;
        }
        //锁定对应的地址 直接循环到最后的地址 取最后一个
        for (int i=0;i < length -k;i++){
            head = head.next;
        }
        return head;
        
    }
}

10、232. 用栈实现队列

题目

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返回队列开头的元素 boolean empty() 如果队列为空,返回 true ;否则,返回 false 说明:

你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

代码

class MyQueue {

    Stack<Integer> s1;
    Stack<Integer> s2;
    public MyQueue() {
        s1 = new Stack();
        s2 = new Stack();
    }
    
    public void push(int x) {
        s1.push(x);
    }
    
    public int pop() {
        if(s2.isEmpty()){
            while(!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    
    public int peek() {
         if(s2.isEmpty()){
            while(!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s1.isEmpty()&&s2.isEmpty();
    }
}

11、9. 回文数

题目

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121 输出:true 示例 2:

输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。 示例 3:

输入:x = 10 输出:false 解释:从右向左读, 为 01 。因此它不是一个回文数。

思路

回文的题目核心一定要想到栈。

代码

class Solution {
    //利用栈的思想判断回文数
    public boolean isPalindrome(int x) {
       boolean flag = true;
       if(x <0){
         return false;
       }
       String s = String.valueOf(x);
       char[] array = s.toCharArray();
       Stack<Character> stack = new Stack();
       for (char a : array){
           stack.push(a);
       }
       for (char a : array){
           char c = stack.pop();
           if (c != a){
               flag = false;
               break;
           }
       }
       return flag;
    }
}

12、876. 链表的中间结点

题目 给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

代码

class Solution {
    public ListNode middleNode(ListNode head) {
       if(head == null){
           return head;
       }
       int length = 0;
       ListNode cur = head;
       while(cur != null){
           cur = cur.next;
           length ++;
       }
       for (int i = 0;i< length/2;i++){
           head = head.next;
       }
       return head;
    }
}

13、83. 删除排序链表中的重复元素

题目

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

image.png 示例 1:

输入:head = [1,1,2] 输出:[1,2]

思路

思路比较简单,移动指针到下一个即可

代码

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
       if (head == null){
           return head;
       }
       ListNode cur = head;
       while(cur != null && cur.next != null){
           ListNode nextNode = cur.next;
           if(cur.val == nextNode.val){
               //注意这里是核心逻辑,如果相等,下一次比较还是当前节点的比较
               cur.next = nextNode.next;
           }else{
               //如果不相等,当前节点的任务完成了
               cur = nextNode;
           }
       }
       return head;
    }
}

13、415. 字符串相加

题目 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:

输入:num1 = "11", num2 = "123" 输出:"134"

思路

代码

class Solution {
    public String addStrings(String num1, String num2) {
         if (num1 == null){
            return "";
        }
        if(num2 == null){
            return "";
        }
        //先转数组
        //从尾部开始加
        //设置一个加的 通过对十取模
        //循环到有一个的length <0 或者 逢10的那个加的为0
        char[] array1 = num1.toCharArray();
        char[] array2 = num2.toCharArray();
        int length1 = array1.length -1 ;
        int length2 = array2.length -1 ;
        int addValue = 0;
        StringBuilder sb = new StringBuilder();
        while (length1 >=0 || length2 >= 0 || addValue >0){
            int k1 = length1 < 0 ? 0 : array1[length1] -'0';
            int k2 = length2 < 0 ? 0 :array2[length2] - '0';
            int add = k1 + k2 + addValue;
            sb.append(add%10);
            addValue = add/10;
            length1 --;
            length2 --;
        }
        return sb.reverse().toString();
        
    }
}

14、136. 只出现一次的数字

题目 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1] 输出: 1

思路 代码

class Solution {
    public int singleNumber(int[] nums) {
        List<Integer> list = new ArrayList<>();
        for (int k : nums){
            if(list.contains(k)){
                list.remove(list.indexOf(k));
            }else{
                list.add(k);
            }
        }
        return list.get(0);
    }
}

15、9. 回文数

题目 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

例如,121 是回文,而 123 不是。  

示例 1:

输入:x = 121 输出:true

思路 代码

class Solution {
    //利用栈的思想判断回文数
    public boolean isPalindrome(int x) {
       if( x< 0){
           return false;
       }
       String s = String.valueOf(x);
       char[] array = s.toCharArray();
       Stack<Character> characters = new Stack<>();
       for (char a : array){
           characters.add(a);
       }
       int index = 0;
       while(!characters.isEmpty()){
           char a = array[index];
           index++;
           if (characters.pop() != a){
               return false;
           }
       }
       return true;
    }
}

16、125. 验证回文串

题目 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

 

示例 1:

输入: "A man, a plan, a canal: Panama" 输出: true 解释:"amanaplanacanalpanama" 是回文串

代码

class Solution {
    public boolean isPalindrome(String s) {
        if(s == null){
            return false;
        }
        char[] array = s.toCharArray();
        StringBuilder sb1 = new StringBuilder();
        for (Character a : array){
            if(Character.isLetterOrDigit(a)){
                sb1.append(Character.toLowerCase(a));
            }
        }
        StringBuilder sb2 = new StringBuilder(sb1).reverse();
        return sb2.toString().equals(sb1.toString());
    }
}

17、 14. 最长公共前缀

题目

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""。

示例 1:

输入:strs = ["flower","flow","flight"] 输出:"fl"

代码

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs == null){
            return "";
        }
        String answer = strs[0];
        //遍历所有的字符串
        for (int i = 1;i< strs.length;i++){
            String tmp = strs[i];
            int length = tmp.length();
            int j= 0;
            for (;j< length && j < answer.length();j++){
                char init = answer.charAt(j);
                char compare = tmp.charAt(j);
                if(init != compare){
                    break;
                }
            }
            answer = answer.substring(0,j);
            if(answer == ""){
                return "";
            }
        }
        return answer;
    }
}

18、459. 重复的子字符串

题目 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

 

示例 1:

输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。

思路

这道题的思路比较有趣,一个串的移动多少次之后都会在 s1 = s + s 里面包含,因此去掉第一位(还未旋转的),去掉最后一位(全部旋转的),再看看是否包含原来的字符串即可

代码

//https://leetcode-cn.com/problems/repeated-substring-pattern/solution/jian-dan-ming-liao-guan-yu-javaliang-xing-dai-ma-s/
//为什么要去掉头尾,头是旋转之前的,尾部是旋转之后的
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        String str = s + s;
        return str.substring(1,str.length()-1).contains(s);
    }
}

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

题目

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组 int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length; for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i]; } 如果所有断言都通过,那么您的题解将被 通过。

示例 1:

输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

思路

题目可以使用快慢指针,慢指针用来指向当前不重复数据的下标,快指针用于遍历,当快指针的数据等于快指针的前一个,则跳过,如果不相等,则放置到慢指针所在的位置

代码

// https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-tudo/
class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 1){
           return 1;
       }
       int slow = 1;
       int fast = 1;
       for (int i = 1;i < nums.length;i++){
           if (nums[fast] != nums[fast-1]){
               nums[slow] = nums[fast];
               slow ++;
               fast ++;
           }else{
               fast ++;
           }
       }
       return slow;
    }
}

20、剑指 Offer 11. 旋转数组的最小数字

题目 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。  

思路 二分查找,但是需要判定一下条件(看下是否数组找靠近中间的都可以考虑下二分)

代码

class Solution {
    public int minArray(int[] numbers) {
        int left = 0;
       int right = numbers.length -1;
       while (left < right){
           int middle = left + (right - left)/2;
           if(numbers[right] < numbers[middle]){
               left = middle +1;
           } else if (numbers[right] > numbers[middle]){
               right = middle;
           }else{
               right = right -1;
           }
       }
       return numbers[left];
    }
}

20、20. 有效的括号

题目 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。  

示例 1:

输入:s = "()" 输出:true 示例 2:

输入:s = "()[]{}" 输出:true

思路

其实就是利用栈的思想,在子串上,是一个类似回文子串,那就可以用栈 代码

class Solution {
    public boolean isValid(String s) {
        if(s == null){
            return false;
        }
        Stack<Character> stack = new Stack<>();
        char[] array = s.toCharArray();
        for(char c : array){
            if(stack.isEmpty()){
                stack.push(c);
            }else{
                char top = stack.peek();
                if((top == '(' && c == ')') || (top == '[' && c == ']')|| (top == '{' && c == '}')){
                    stack.pop();
                }else{
                    stack.push(c);
                }
            }
        }
        return stack.isEmpty();
    }
}

21、155. 最小栈

题目 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) —— 将元素 x 推入栈中。 pop() —— 删除栈顶的元素。 top() —— 获取栈顶元素。 getMin() —— 检索栈中的最小元素。

思路

代码

class MinStack {

        Stack<Integer> noraml ;
        Stack<Integer> min ;

        public MinStack() {
            noraml = new Stack<>();
            min = new Stack<>();
        }

        public void push(int val) {
           noraml.push(val);
           if(min.isEmpty()){
               min.push(val);
           }else{
               int top = min.peek();
               if (top >= val){
                   min.push(val);
               }
           }
        }

        public void pop() {
            int value = noraml.pop();
            int top = min.peek();
            if(value <= top){
                min.pop();
            }
        }

        public int top() {
            return noraml.peek();
        }

        public int getMin() {
            return min.peek();
        }
    }

22、283. 移动零

题目 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

 

示例 1:

输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]

思路 利用快慢指针,如果发现快指针不为0,则跟慢指针替换,替换后慢指针往前走。 整体替换完成后,要从慢指针往后开始改成0 代码

class Solution {
    public void moveZeroes(int[] nums) {
    
        int slow = 0;
        int fast = 0;
        int length = nums.length;
        for (int i = 0;i < length;i++){
            int slowValue = nums[slow];
            int fastValue = nums[fast];
            if(fastValue != 0){
                nums[slow] = fastValue;
                slow++;
            }
            fast++;
        }
        for (int i = slow;i< length ;i++){
            nums[i] = 0;
        }
    }
}

23、剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

 

示例:

输入:nums = [1,2,3,4] 输出:[1,3,2,4] 注:[3,1,2,4] 也是正确的答案之一。

思路

双指针思路,比较左右两边的值是否需要替换。

代码

class Solution {
    public void moveZeroes(int[] nums) {
    
        int slow = 0;
        int fast = 0;
        int length = nums.length;
        for (int i = 0;i < length;i++){
            int slowValue = nums[slow];
            int fastValue = nums[fast];
            if(fastValue != 0){
                nums[slow] = fastValue;
                slow++;
            }
            fast++;
        }
        for (int i = slow;i< length ;i++){
            nums[i] = 0;
        }
    }
}

24、7. 整数反转

题目 给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。  

示例 1:

输入:x = 123 输出:321

思路

代码有点问题的。。。

代码

class Solution {
    public int reverse(int x) {
        int result = 0;
        while(x  != 0 ){
            int value = x % 10;
            result =  result * 10 + value;
            x = x/10;

        }
        return result;
    }
}

25、 349. 两个数组的交集

题目

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

 

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2] 示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的

思路

代码

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
       HashSet<Integer> h1 = new HashSet<>();
        HashSet<Integer> h2 = new HashSet<>();
        HashSet<Integer> h3 = new HashSet<>();
        for (int v : nums1){
            h1.add(v);
        }
        for (int v : nums2){
            h2.add(v);
        }
        Iterator<Integer> i1 = h1.iterator();
        while(i1.hasNext()){
            int val = i1.next();
            if(h2.contains(val)){
                h3.add(val);
            }
        }
        int [] answer = new int [h3.size()];
        int i = 0;
        Iterator<Integer> i3 = h3.iterator();
        while(i3.hasNext()){
            int value = i3.next();
            answer[i] = value;
            i ++;
        }
        return answer;
    }
}

26、1047. 删除字符串中的所有相邻重复项

题目 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

 

示例:

输入:"abbaca" 输出:"ca"

代码

class Solution {
    public String removeDuplicates(String s) {
      if(s == null){
          return "";
      }
      char[] array = s.toCharArray();
      Stack<Character> stack = new Stack();
      for (char value : array){
        //如果栈开始就是空的 入栈
        if(stack.isEmpty()){
            stack.push(value);
        }else{
            //如果栈不是空的,比较栈顶跟即将入栈的值,如果相同,则出栈,如果不相同,则入栈
            if(value == stack.peek()){
                stack.pop();
            }else{
                stack.push(value);
            }
        }
      }
      StringBuilder sb = new StringBuilder();
      while(!stack.isEmpty()){
          sb.append(stack.pop());
      }
      return sb.reverse().toString();
    }
}

27、剑指 Offer 61. 扑克牌中的顺子

题目 从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

输入: [0,0,1,2,5]
输出: True
输入: [1,2,3,4,5]
输出: True

思路

核心思路:比较最大值与最小值是否超过5,如果是则有2个大小王也没有办法凑成顺子,另外就是看是否有重复的,如果除了大小王还有重复的。注意下最小的值,不能写成0,应该写成>13的数字。

代码

class Solution {
    public boolean isStraight(int[] nums) {
        List<Integer> list = new ArrayList();
        int max = 0;
        int min = 14;
        for(int i = 0;i< nums.length;i++){
            int value = nums[i];
            if (value == 0){
                continue;
            }
            if(list.contains(value)){
                return false;
            }
            list.add(value);
            max = Math.max(value,max);
            min = Math.min(value,min);
        }
        return max - min < 5;
    }
}

28、344. 反转字符串

题目

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

 

示例 1:

输入:s = ["h","e","l","l","o"] 输出:["o","l","l","e","h"]

代码

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

29、 1. 两数之和

题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

 

示例 1:

输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路

代码

class Solution {
    public int[] twoSum(int[] nums, int target) {
       HashMap<Integer,Integer> map = new HashMap();
       for (int i = 0;i < nums.length;i++){
           //计算差额
           int compare = target - nums[i];
           //如果差额在map中已经存在,则返回 
           boolean exist = map.get(compare) == null? false:true;
           if(exist){
               return new int[]{map.get(compare),i};
           }else{
               map.put(nums[i],i);
           }
       }
       return new int[]{};
    }
}

30、144. 二叉树的前序遍历

题目

给你二叉树的根节点 root ,返回它节点值的 前序 **遍历。

思路

本质上是把二叉树进行拆分,因为是中-左-右的思路,所以需要按照中、右、左的思路出栈。

代码

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
       List<Integer> list = new ArrayList();
       if(root == null){
           return list;
       }
       Stack<TreeNode> stack = new Stack<TreeNode>();
       stack.push(root);
       while(!stack.isEmpty()){
           //先加入头节点(先弹出的节点)
           TreeNode node = stack.pop();
           list.add(node.val);
           if(node.right != null){
               stack.push(node.right);
           }
           if(node.left != null){
               stack.push(node.left);
           }
       }
       return list;
    }
}

31、268. 丢失的数字

题目

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

 

示例 1:

输入:nums = [3,0,1] 输出:2 解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

思路

代码

class Solution {
    public int missingNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums){
            set.add(num);
        }
        for (int i = 0;i < nums.length;i++){
            int compare = nums.length - i;
            if(!set.contains(compare)){
               return compare;
            }
        }
        return 0;
    }
}

32、 557. 反转字符串中的单词 III

题目 给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

 

示例 1:

输入:s = "Let's take LeetCode contest" 输出:"s'teL ekat edoCteeL tsetnoc"

代码

class Solution {
    public String reverseWords(String s) {
        String [] array = s.split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = 0;i< array.length;i++){
            char[] arr = array[i].toCharArray();
            int left = 0;
            int right = arr.length -1;
            while(left < right){
                char temp = arr[left];
                arr[left] = arr[right];
                arr[right] = temp;
                left ++;
                right --;
            }
            for (char a : arr){
                sb.append(a);
            }
            sb.append(" ");
        }
        return sb.substring(0,sb.length()-1).toString();
    }
}

33、 94. 二叉树的中序遍历

题目

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

思路

一直往左走,再往上走,再往右走。

代码

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList();
        if(root == null){
            return list;
        }
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack();
        while(!stack.isEmpty() || cur != null){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            list.add(cur.val);
            cur = cur.right;
        }
        return list;
    }
}