算法常见习题

文章目录

    • 1.二维数组中的查找
    • 2.旋转数组的最小数字
    • 3.调整数组顺序使奇数位于偶数前面
      • 3.1相对顺序不做要求
      • 3.2保证奇数和奇数,偶数和偶数之间的相对位置不变
    • 4.数组中出现次数超过一半的数字
    • 5.将空格替换为“%20”
    • 6.从尾到头打印链表
    • 7.重建二叉树
    • 8.斐波那契数列
    • 9.青蛙跳台阶的问题
    • 10.矩形覆盖
    • 11.二进制中1的个数
    • 12.链表中倒数第k个结点
    • 13.翻转链表
    • 14.合并链表
    • 15.子树判断
    • 16.二叉树的镜像
    • 17.删除链表中重复的结点
    • 18.包含min函数的栈
    • 19.判断是不是栈的弹出序列
    • 20.层序遍历二叉树
    • 21.是否是二叉搜索树后续遍历的结果
    • 22.二叉树中和为某一值的路径
    • 23.字符串的排序
    • 24.top-k问题(前k个最小元素)
    • 25.连续子序列的最大和
    • 26.回文判断
    • 27.把数组排成最小的数
    • 28.两个链表的第一个公共结点
    • 29.二叉树的深度
    • 30.数组中只出现一次的数字
    • 31.和为s的连续正数序列
    • 32.左旋转字符串
    • 33.翻转单词序列
    • 34.按之字形顺序打印二叉树
    • 35. 二叉搜索树的第k个节点

1.二维数组中的查找

本题掌握思想:查找的过程实际是排除的过程
解法一:一次排除一行

public class Solution {
    public boolean Find(int target, int [][] array) {
        int i = 0;//行
        int j = array[0].length - 1;//列
        // [
        // [1,2,8,9],
        // [2,4,9,12],
        // [4,7,10,13],
        // [6,8,11,15]
        // ]
        //i和j都不能越界,此时array[i][j]在矩阵的右上角的位置(9),是这一行的最大值,这一列的最小值
        while(i < array.length && j >= 0){
            //如果此时的值比要找的值大,说明不可能在这一列
            if(target < array[i][j]){
                j--;
            }else if(target > array[i][j]){
                //如果此时的值比要找的值小,说明不可能在这一行
                i++;
            }else{
                // 找到了
                return true;
            }
        }
        // 遍历完成,没有找到
        return false;
    }
}

解法二:直接查找

public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int i = 0;i < array.length;i++){
            for(int j = 0;j < array[i].length;j++){
                if(array[i][j] == target){
                    return true;
                }
            }
        }
        return false;
    }
}

2.旋转数组的最小数字

解法一:二分查找

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        //数组中没有元素
        if(array == null || array.length == 0){
            return 0;
        }
        int left = 0;//数组的第一个元素的下标
        int rigth = array.length - 1;//数组的最后一个元素的下标
        int mid = 0;//数组中间的元素
        while(array[left] >= array[rigth]){
            //数组中只有两个元素,最小值是右边的值
            if(rigth - left == 1){
                mid = rigth;
                break;
            }
            mid =  (rigth + left) >> 1;
            // 数组中的中间元素和两边的元素都相等,采用线性查找
            if(array[left] == array[rigth] && array[left] == array[mid]){
                int result = array[left];
                for(int i = left + 1;i < rigth;i++){
                    if(result > array[i]){
                        result = array[i];
                    }
                }
                return result;
            }
            // 如果中间的值大于最左侧的值,最小值一定在右侧
            if(array[mid] >= array[left]){
                left = mid;
            }else {
                // 如果中间的值小于最右侧的值,最小值一定在左侧
                rigth = mid;
            }
        }
        return array[mid];
    }
}

解法二:直接查找

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int num = array[0];
        for(int i = 1;i < array.length;i++){
            if(array[i] < num){
                num = array[i];
            }
        }
        return num;
    }
}

3.调整数组顺序使奇数位于偶数前面

3.1相对顺序不做要求

import java.util.*;
public class Solution {
    public int[] reOrderArrayTwo (int[] array) {
        int left = 0;//数组的第一个元素的下标
        int right = array.length - 1;//数组的最后一个元素的下标
        while(left < right){
            // 前面找到偶数,不是奇数,left就往后移动
            while(left < right && array[left] % 2 == 1){
                left++;
            }
            // 后面找到奇数,不是偶数,right就往前移动
            while(left < right && array[right] % 2 == 0){
                right--;
            }
            // 交换找到的奇数和偶数
            if(left < right){
                int tmp = array[left];
                array[left] = array[right];
                array[right] = tmp;
            }
        }
        return array;
    }
}

3.2保证奇数和奇数,偶数和偶数之间的相对位置不变

public class Solution {
    public void reOrderArray(int [] array) {
        if(array.length == 0 || array == null){
            return;
        }
        int k = 0;
        for(int i = 0;i < array.length;i++){
            // 找到奇数
            if(array[i] % 2 == 1){
                // 保存找到的奇数
                int tmp = array[i];
                // 将找到的奇数和已经确定位置的奇数之间的数整体向后移动
                int j = i;
                while(j > k){
                    array[j] = array[j - 1];
                    j--;
                }
                // 将保留的奇数放到移动后空下来的位置
                array[k] = tmp;
                // k从0开始是保存奇数的位置,保存完后k要向后移动
                k++;
            }
        }
    }
}

4.数组中出现次数超过一半的数字

解法一:HashMap

import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        // 定义一个map保存数组中各个元素出现的次数
        HashMap<Integer,Integer> map = new HashMap<>();
        int n = array.length;
        for(int i = 0;i < n;i++){
            // 如果map中已经包含了这个数字就将该数字的次数+1
            if(map.containsKey(array[i])){
                map.put(array[i],map.get(array[i]) + 1);
            }else{
                // 如果map中没有包含了这个数字保存并次数是1
                map.put(array[i],1);
            }
        }
        // 遍历数组找到出现次数超过一半的次数
        for(int i = 0;i < n;i++){
            if(map.get(array[i]) > n/2){
                return array[i];
            }
        }
        return 0;
    }
}

解法二:先排序后找

import java.util.*;

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0){
            return 0;
        }
        // 将数组进行排序,如果有出现次数超过一半的数字一定是在数组的中间
        Arrays.sort(array);
        int num = array[array.length / 2];
        int count = 0;
        // 排序后数组中间的数字出现的次数
        for(int i = 0;i < array.length;i++){
            if(array[i] == num){
                count++;
            }
        }
        if(count > array.length / 2){
            return num;
        }else{
            return 0;
        }
    }
}

解法三:抵消法

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length == 0 || array == null){
            return 0;
        }
        int target = array[0];
        int times = 1;//当前元素出现的次数
        for(int i = 1;i < array.length;i++){
            // 如果times==0说明i之前的元素全部抵消了,就定义新的可能出现次数超过一半的元素等于target
            if(times == 0){
                target = array[i];
                times = 1;
            }else if(target == array[i]){
                // 如果当前元素的值等于target就让他出现的次数+1
                times++;
            }else{
                // 如果当前元素的值不等于target就让他出现的次数-1
                times--;
            }
        }
        // 让不相等的两个元素相互抵消,最终target指向的元素就是出现次数超过一半的元素
        // 统计target出现的次数
        int count = 0;
        for(int i = 0;i < array.length;i++){
            if(target == array[i]){
                count++;
            }
        }
        if(count > array.length/2){
            return target;
        }else{
            return 0;
        }
    }
}

5.将空格替换为“%20”

解法一:在原有的字符串上进行改变

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int count = 0;
        // 统计空格出现的次数
        for(int i = 0;i < str.length();i++){
            if(str.charAt(i) == ' '){
                count++;
            }
        }
        // 定义新的字符串的长度(由于是一个字符串(空格)被替换为三个字符串(%20),所以新的字符串的长度就是原来的字符串的长度加上空格的个数的二倍)
        int newLength = str.length() + 2 * count;
        // 找到新的字符串的最后一个位置
        int newEnd = newLength - 1;
        // 找到原来的字符串的最后一个位置
        int oldEnd = str.length() - 1;
        // 更新字符串的长度(变长了)
        // setLength(int newLength):设置字符序列的长度
        str.setLength(newLength);
        // 进行替换
        while(newEnd >= 0 && oldEnd >= 0){
            // 如果是空格就进行替换,每替换一个就让enwEnd向前走一步,替换完成以后让oldEnd向前走一步
            // setCharAt(int index, char ch):指定索引处的字符设置为ch 
            if(str.charAt(oldEnd) == ' '){
                str.setCharAt(newEnd--,'0');
                str.setCharAt(newEnd--,'2');
                str.setCharAt(newEnd--,'%');
                oldEnd--;
            }else{
                // 如果不是空格就直接将原来的字符进行拼接
                str.setCharAt(newEnd,str.charAt(oldEnd));
                newEnd--;
                oldEnd--;
            }
        }
        //toString():返回表示此顺序中的数据的字符串
        return str.toString();
    }
}

解法二:直接将添加后的字符串放到一个新的字符串上

public class Solution {
    public String replaceSpace(StringBuffer str) {
    	StringBuffer sb = new StringBuffer();
        for(int i = 0;i < str.length();i++){
            if(str.charAt(i) == ' '){
                sb.append("%20");
            }else{
                sb.append(str.charAt(i));
            }
        }
        return sb.toString();
    }
}

6.从尾到头打印链表

解法一:栈

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        // 将链表中的值全部添加到栈中
        while(listNode != null){
            stack.push(listNode.val);
            listNode = listNode.next;
        }
        // 将栈中的元素全部添加到顺序表中
        while(!stack.empty()){
            list.add(stack.pop());
        }
        return list;
    }
}

解法二:递归

import java.util.*;
public class Solution {
    // 递归实现链表的从尾到头的打印
    public static void printListFromTailToHeadChild(ListNode listNode,ArrayList<Integer> list){
        // 递归的出口是链表结点为空
        if(listNode == null){
            return;
        }
        printListFromTailToHeadChild(listNode.next,list);
        // 将链表节点的值添加到顺序表中
        list.add(listNode.val);
    }
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        printListFromTailToHeadChild(listNode,list);
        return list;
    }
}

解法三:逆置打印顺序表

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        // 将链表之中的元素添加到顺序表中
        while(listNode != null){
            list.add(listNode.val);
            listNode = listNode.next;
        }
        int i = 0;
        int j = list.size() - 1;
        // 逆置顺序表中的元素
        while(i < j){
            int tmp = list.get(i);
            list.set(i,list.get(j));
            list.set(j,tmp);
            i++;
            j--;
        }
        return list;
    }
}

7.重建二叉树

import java.util.*;
public class Solution {
    public static TreeNode reConstructBinaryTreeChild(int[] pre, int preStart, int preEnd, int[] vin, int vinStart, int vinEnd) {
        // 递归出口
        if (preStart > preEnd || vinStart > vinEnd) {
            return null;
        }
        // 构建根结点
        TreeNode root = new TreeNode(pre[preStart]);
        // 遍历中序遍历结果找到根结点
        for (int i = vinStart; i <= vinEnd; i++) {
            if (vin[i] == root.val) {
                // 构建左子树
                // preStart + 1 + i - vinStart - 1
                root.left = reConstructBinaryTreeChild(pre, preStart + 1,preStart + i - vinStart, vin, vinStart, i - 1);
                // 构建右子树
                root.right = reConstructBinaryTreeChild(pre, preStart + i - vinStart + 1,preEnd, vin, i + 1, vinEnd);
                break;
            }
        }
        return root;
    }
    public TreeNode reConstructBinaryTree(int[] pre, int[] vin) {
        if (pre == null || vin == null) {
            return null;
        }
        return reConstructBinaryTreeChild(pre, 0, pre.length - 1, vin, 0, vin.length - 1);
    }
}

8.斐波那契数列

解法一:迭代(动态规划)

public class Solution {
    public int Fibonacci(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        int first = 1;
        int second = 1;
        int third = 0;
        while(n > 2){
            third = first + second;
            first = second;
            second = third;
            n--;
        }
        return third;
    }
}

解法二:递归

public class Solution {
    public int Fibonacci(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}

解法三:剪枝

import java.util.*;

public class Solution {
    // map中存的第一个是斐波那契数的项数,第二个数是该项数对应的值
    private static HashMap<Integer, Integer> map = new HashMap<>();
    public int Fibonacci(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        // 求要求的项数的前两项
        int ppre = 0;
        if (map.containsKey(n - 2)) {
            ppre = map.get(n - 2);
        } else {
            ppre = Fibonacci(n - 2);
            map.put(n - 2,ppre);
        }
        // 求要求的项数的前一项
        int pre = 0;
        if (map.containsKey(n - 1)) {
            pre = map.get(n - 1);
        } else {
            pre = Fibonacci(n - 1);
            map.put(n - 1,pre);
        }
        return ppre + pre;
    }
}

9.青蛙跳台阶的问题

解决动态规划问题的步骤:

  1. 定义状态
  2. 编写状态转移方程
  3. 设置初始值

本题中:

  1. 状态:f(n)表示青蛙跳到第n级台阶跳法的总次数
  2. 状态转移方程:f(n) = f(n-1) + f(n-2)
  3. 初始值:f(0) = 1;f(1) = 1;f(2) = 2;
public class Solution {
    public int jumpFloor(int target) {
        if(target <= 2){
            return target;
        }
        // 定义一个数组
        int[] dp = new int[target + 1];
        // 设置初始值
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3;i <= target;i++){
            // 状态转移方程
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[target];
    }
}

10.矩形覆盖

public class Solution {
    // 状态:f(n)就是有n个2*1的小矩阵覆盖一个2*n的打矩阵有多少中覆盖方法
    // 状态转移方程:f(n) = f(n-1) + f(n-2)
    // 此时f(n-1)表示最后一个竖直放;f(n-2)表示最后两个横着放
    // 初始值:f(1) = 1;f(2) = 2
    public int rectCover(int target) {
        if(target <= 2){
            return target;
        }
        int[] dp = new int[target + 1];
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3;i <= target;i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[target];
    }
}

11.二进制中1的个数

解法一:与前一位数进行按位与

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        // 每一次和它前面的一个数按位与都会消掉一个1
        // 例如:
        // n:   1000 1100
        // n-1: 1000 1011 &
        // ---------------------
        //      1000 1000
        // ---------------------
        // n:   1000 1000
        // n-1: 1000 0111 &
        // ---------------------
        //      1000 0000
        // ---------------------
        // n:   1000 0000
        // n-1: 0111 1111 &
        // ---------------------
        //      0000 0000
        while (n != 0) {
            n &= n - 1;
            count++;
        }
        return count;
    }
}

解法二:与1进行按位与

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n != 0){
            if((n & 1) == 1){
                count++;
            }
            n >>>= 1;
        }
        return count;
    }
}

12.链表中倒数第k个结点

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null || k < 0){
            return null;
        }
        // 定义两个结点都指向头结点
        ListNode fast = head;
        ListNode slow = head;
        // 先让快指针走k步
        for(int i = 0;i < k;i++){
            if(fast == null){
                return null;
            }
            fast = fast.next;
        }
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

13.翻转链表

解法一:头插法进行翻转

public class Solution {
    public ListNode ReverseList(ListNode head) {
        // 特殊情况,只有一个节点或者没有节点
        if(head == null || head.next == null){
            return head;
        }
        // 定义一个新的链表的头结点
        ListNode newHead = null;
        while(head != null){
            // 拿到当前链表的头结点
            ListNode cur = head;
            // 然当前链表向后走
            head = head.next;
            // 将当前链表的头结点进行头插到新的链表
            cur.next = newHead;
            // 修改新的链表头结点的指向
            newHead = cur;
        }
        return newHead;
    }
}

解法二:定义三个指针,依次翻转

public class Solution {
    public ListNode ReverseList(ListNode head) {
        // 特殊情况,只有一个节点或者没有节点
        if(head == null || head.next == null){
            return head;
        }
        // 定义一个要翻转的结点指向头结点的下一个节点
        ListNode cur = head.next;
        // 将头结点置为空
        head.next = null;
        while(cur != null){
            // 定义要翻转的结点的下一个节点
            ListNode curNext = cur.next;
            // 修改翻转节点的指向
            cur.next = head;
            // 将三个节点向后移动
            head =cur;
            cur = curNext;
        }
        return head;
    }
}
public class Solution {
    public ListNode ReverseList(ListNode head) {
        // 特殊情况,只有一个节点或者没有节点
        if(head == null || head.next == null){
            return head;
        }
        // 定义一个要翻转的结点指向头结点的下一个节点
        ListNode left = head;
        ListNode mid = left.next;
        ListNode right = mid.next;
        while(right != null){
            mid.next = left;
            left = mid;
            mid = right;
            right = right.next;
        }
        mid.next = left;
        head.next = null;
        return mid;
    }
}

14.合并链表

解法一:合并

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        // 两张链表都为空
        if(list1 == null && list2 == null){
            return null;
        }
        // 定义一个虚拟节点
        ListNode node = new ListNode(-1);
        ListNode tmp = node;
        // 两张链表都不为空
        while(list1 != null && list2 != null){
            // 第一个链表的值小,就将第一个链表的当前结点尾插进入合并后的链表
            if(list1.val < list2.val){
                tmp.next = list1;
                list1 = list1.next;
                tmp = tmp.next;
            }else{
                tmp.next = list2;
                list2 = list2.next;
                tmp = tmp.next;
            }
        }
        // 第二个链表还不为空,就将剩下的结点全部尾插
        if(list1 == null){
            tmp.next = list2;
        }
        // 第一个链表还不为空,就将剩下的结点全部尾插
        if(list2 == null){
            tmp.next = list1;
        }
        return node.next;
    }
}

解法二:递归

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        ListNode node = new ListNode(-1);
        if(list1.val < list2.val){
            node.next = list1;
            list1 = list1.next;
            node = node.next;
        }else{
            node.next = list2;
            list2 = list2.next;
            node = node.next;
        }
        node.next = Merge(list1,list2);
        return node;
    }
}

15.子树判断

public class Solution {
    // 判断两棵子树的左右子树是否想等(已经找到根结点)
    public boolean isSameChild(TreeNode root1,TreeNode root2){
        // 如果待比较的数以及为空,说明已经比较完成了
        if(root2 == null){
            return true;
        }
        // 如果去比较的子树为空,说明待比较的子树还未比较完,说明不是子树
        if(root1 == null){
            return false;
        }
        // 如果两棵树的值不相等,说明不是子树
        if(root1.val != root2.val){
            return false;
        }
        // 再比较根结点下的左右子树是否相同
        return isSameChild(root1.left,root2.left) && isSameChild(root1.right,root2.right);
    }
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        // 有两棵树中任意一棵为空,则不是子树
        if(root1 == null || root2 == null){
            return false;
        }
        boolean result = false;
        // 找到根结点相等的结点
        if(root1.val == root2.val){
            result = isSameChild(root1,root2);
        }
        // 如果此时还不是子树,就看根结点下的左右子树是不是子树
        if(result != true){
            result = HasSubtree(root1.left,root2);
        }
        if(result != true){
            result = HasSubtree(root1.right,root2);
        }
        return result;
    }
}

16.二叉树的镜像

public class Solution {
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot == null){
            return null;
        }
        // 递归出口:左右结点的都为空
        if(pRoot.left == null && pRoot.right == null){
            return pRoot;
        }
        // 交换根结点的左右子树
        TreeNode tmp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = tmp;
        // 将根结点的左右子树进行镜像
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}

17.删除链表中重复的结点

解法一:

public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if (pHead == null || pHead.next == null) {
            return pHead;
        }
        ListNode node = new ListNode(-1);
        node.next = pHead;
        ListNode cur = node;
        ListNode curNext = cur.next;
        while (curNext != null) {
            // 先找到重复的范围
            while (curNext.next != null && curNext.val != curNext.next.val) {
                curNext = curNext.next;
                cur = cur.next;
            }
            // 在重复的范围内找
            while (curNext.next != null && curNext.val == curNext.next.val) {
                curNext = curNext.next;
            }
            // 去重
            if (cur.next != curNext) {
                cur.next = curNext.next;
            }
            // 保证curNext始终在cur的后一个节点
            curNext = curNext.next;
        }
        return node.next;
    }
}

解法二:

public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if(pHead == null || pHead.next == null){
            return pHead;
        }
        ListNode node = new ListNode(-1);
        node.next = pHead;
        ListNode tmp = node;
        ListNode cur = node.next;
        boolean flg = false;
        while(cur.next != null && cur != null){
            if(cur.val == cur.next.val){
                cur.next = cur.next.next;
                flg = true;
            }else{
                // 需要删除重复节点
                if(flg){
                    tmp.next = cur.next;
                    flg = false;
                }else{
                    // 不需要删除重复节点,只是需要向后移动
                    tmp = tmp.next;
                }
                cur = cur.next;
            }
        }
        // 最后一个节点需要删除
        if(flg){
            tmp.next = null;
        }
        return node.next;
    }
}

18.包含min函数的栈

import java.util.Stack;
public class Solution {
    public static Stack<Integer> stack = new Stack<>();//数据栈
    public static Stack<Integer> minStack = new Stack<>();//辅助栈(最小数据栈)
    public void push(int node) {
        // 只要最小栈为空或者最小栈栈顶元素大于要入栈的元素就将该元素也入最小栈
        if(minStack.empty() || minStack.peek() > node){
            minStack.push(node);
        }else{
            // 否则就将该最小栈栈顶元素在入栈一次
            minStack.push(minStack.peek());
        }
        stack.push(node);
    }
    // 弹出栈顶元素
    public void pop() {
        stack.pop();
        minStack.pop();
    }
    // 获取栈顶元素
    public int top() {
        return stack.peek();
    }
    // 获取栈中最小元素
    public int min() {
        return minStack.peek();
    }
}

19.判断是不是栈的弹出序列

import java.util.*;

public class Solution {
    public boolean IsPopOrder(int [] pushA, int [] popA) {
        if (pushA == null || popA == null || pushA.length != popA.length) {
            return false;
        }
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        // 遍历要入栈的序列
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            // 当栈为空且栈顶元素等于弹出序列的元素时,就将元素弹出
            while (!stack.empty() && stack.peek() == popA[j]) {
                stack.pop();
                j++;
            }
        }
        // 最后如果栈为空则是弹出顺序,反之则不是
        return stack.empty();
    }
}

20.层序遍历二叉树

import java.util.*;
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        // 将数的根结点入队列
        queue.offer(root);
        while(!queue.isEmpty()){
            // 获取并删除队头位置的元素
            TreeNode father = queue.poll();
            // 将获取到的元素的值添加到顺序表中
            list.add(father.val);
            // 如果此时队头位置元素的左树右树不为空,就将队头位置元素的左树右树入队列
            if(father.left != null){
                queue.offer(father.left);
            }
            if(father.right != null){
                queue.offer(father.right);
            }
        }
        return list;
    }
}

21.是否是二叉搜索树后续遍历的结果

public class Solution {
    public boolean VerifySquenceOfBSTChild(int[] sequence,int start,int end){
        // 递归出口,数组已经遍历完成,说明是二叉搜索树的后续遍历结果
        if(start >= end){
            return true;
        }
        int i = start;
        // 找到当前的根结点
        int root = sequence[end];
        // 从start位置开始找到比当前根结点大的数,得到左子树
        while(i < end && sequence[i] < root){
            i++;
        }
        // 遍历右子树,如果有比当前根结点小的数字,说明不是二叉搜索树的后续遍历结果
        for(int j = i;j < end;j++){
            if(sequence[j] < root){
                return false;
            }
        }
        // 此时在看根结点的左右子树是否符合要求
        return VerifySquenceOfBSTChild(sequence,start,i-1) && VerifySquenceOfBSTChild(sequence,i,end - 1);
    }
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence == null || sequence.length == 0){
            return false;
        }
        return VerifySquenceOfBSTChild(sequence,0,sequence.length - 1);
    }
}

22.二叉树中和为某一值的路径

import java.util.ArrayList;
public class Solution {
    public void FindPathDFS(TreeNode root, int expectNumber,ArrayList<ArrayList<Integer>> result, ArrayList<Integer> list) {
        if (root == null) {
            return;
        }
        // 将当前值放入list待选结果集当中
        list.add(root.val);
        // 更新目标值(由于已经添加了当前值,所以目标值就更新为原来的值减去当前值)
        expectNumber -= root.val;
        // 如果当前结点已经是叶子结点且目标值为0(也就是待选结果集中的值之和等于目标值),就将待选结果集添加到最终结果集中)
        if (root.left == null && root.right == null && expectNumber == 0) {
            result.add(new ArrayList<>(list));
            // result.add(list);
        }
        // DFS:深度优先遍历递归统计
        FindPathDFS(root.left, expectNumber, result, list);
        FindPathDFS(root.right, expectNumber, result, list);
        // 回溯过程
        list.remove(list.size() - 1);
    }
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int expectNumber) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();//最终结果集
        ArrayList<Integer> list = new ArrayList<>();//待选结果集
        FindPathDFS(root, expectNumber, result, list);
        return result;
    }
}

23.字符串的排序

import java.util.*;
public class Solution {  
    // 交换函数
    public void swap(int start,int end,char[] ch){
        char tmp = ch[start];
        ch[start] = ch[end];
        ch[end] = tmp;
    }
    public void PermutationHelper(char[] ch,int start,ArrayList<String> list){
        // 递归出口,start此时指向最后一个字符
        if(start == ch.length - 1){
            // 去重
            if(!list.contains(String.valueOf(ch))){
                // String str = new String(ch);
                list.add(new String(ch));
            }
            return;
        }
        // 遍历字符数组,然每一个字符都做第一个字符
        for(int i = start;i < ch.length;i++){
            // start指向的字符就是当前作为排序的第一个字符
            swap(start,i,ch);
            PermutationHelper(ch,start + 1,list);
            swap(start,i,ch);
        }
    }
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<>();
        if (str != null && str.length() > 0) {
            PermutationHelper(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
}

24.top-k问题(前k个最小元素)

import java.util.*;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if(input == null || input.length < k || k <= 0){
            return list;
        }
        // priorityQueue:优先级队列(底层使用的是堆),默认创建的是最小堆
        PriorityQueue<Integer> queue = new PriorityQueue<>(k,Collections.reverseOrder());//创建最大堆
        for(int i = 0;i < input.length;i++){
            // 先将k个数字放到queue中,其内部会进行自动排序(最大堆是降序排列)
            if(i < k){
                queue.offer(input[i]);
            }else{
                // 如果当前元素比优先级队列元素小,就对其进行更新(每次删除的都是当前队列中最大的)
                if(input[i] < queue.peek()){
                    queue.poll();
                    queue.offer(input[i]);
                }
            }
        }
        // 遍历队列,将队列中的元素添加到list中
        while(!queue.isEmpty()){
            list.add(queue.poll());
        }
        return list;
    }
}

25.连续子序列的最大和

解法一:动态规划

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        // 定义状态:f(i)是以i结尾的连续子序列的最大和
        // 状态方程:f(i) = max(f(i-1) + array[i],array[i])
        // 初始值:f(0) = array[0]
        int[] dp = new int[array.length];
        dp[0] = array[0];
        int max = dp[0];
        for(int i = 1;i < array.length;i++){
            dp[i] = Math.max(array[i],dp[i - 1] + array[i]);
            // 当前值大于最大值就进行更新
            if(dp[i] > max){
                max = dp[i];
            }
        }
        return max;
    }
}

解法二:

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int total = array[0];
        int max_value = array[0];
        //for 循环,用来检测以i下标结尾的连续子序列的和
        for (int i = 1; i < array.length; i++) {
            if (total >= 0) {
            //如果之前total累计的和>=0,说明当前数据+total,有利于整体增大
                total += array[i];
            } else {
            //如果之前累计的和<0,说明当前数据+total,不利于整体增大,丢弃之前的所有值
                total = array[i];
            }
            if (max_value < total) {
                max_value = total;
            }
        }
        return max_value;
    }
}

26.回文判断

import java.util.Scanner;
public class Main {
    public static int isPalindrome(StringBuffer sb) {
        int start = 0;//起始位置
        int end = sb.length() - 1;//终止位置
        while (start < end) {
            // 如果两边不等,就删除
            if (sb.charAt(start) != sb.charAt(end)) {
                // 如果判断下一个位置和当前后面的位置相等就删除前边的
                if (sb.charAt(start + 1) == sb.charAt(end)) {
                    return start;
                } else {
                    // 否则就删除后面的
                    return end;
                }
            } else{
	            start++;
	            end--;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        sc.nextLine();
        while (sc.hasNext()) {
            StringBuffer sb = new StringBuffer(sc.nextLine());
            System.out.println(isPalindrome(sb));
        }
    }
}

27.把数组排成最小的数

import java.util.*;

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        // 将待比较的元素全部添加到list中
        ArrayList<Integer> list = new ArrayList<>();
        for(int i = 0;i < numbers.length;i++){
            list.add(numbers[i]);
        }
        // 给list集合进行排序
        Collections.sort(list,new Comparator<Integer>(){
            // 重写比较函数,这里,如果xs > ys 返回1;xs < ys 返回-1;xs == ys 返回0
            @Override
            public int compare(Integer x,Integer y){
                String xs = x + "" + y;
                String ys = y + "" + x;
                return xs.compareTo(ys);
            }
        });
        // 按照排序后的结果进行拼接
        String result = new String();
        for(Integer x : list){
            result += x;
        }
        return result;
    }
}

28.两个链表的第一个公共结点

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode pl = pHead1;
        ListNode ps = pHead2;
        int len1 = 0;
        int len2 = 0;
        // 统计两个链表的长度
        while(pl != null){
            pl = pl.next;
            len1++;
        }
        while(ps != null){
            ps = ps.next;
            len2++;
        }
        // 修改指向
        pl = pHead1;
        ps = pHead2;
        // 算出两个链表长度的差值
        int len = len1 - len2;
        // 让pl始终指向较长的链表
        if(len < 0){
            pl = pHead2;
            ps = pHead1;
            len = -len;
        }
        // 让较长的链表走len步
        while(len != 0 && pl != null){
            pl = pl.next;
            len--;
        }
        // 让两个链表同时往后走直到两个链表相遇
        while(pl != null && ps != null){
            if(ps == pl){
                return ps;
            }
            ps = ps.next;
            pl = pl.next;
        }
        return null;
    }
}

29.二叉树的深度

解法一:递归

public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        // 二叉树的深度就是等于根结点的左树和右树深度的最大值加上当前结点的深度(1)
        return Math.max(TreeDepth(root.left) + 1,TreeDepth(root.right) + 1);
    }
}

解法二:递归

public class Solution {
    private int maxDepth;
    public void TreeDepthMax(TreeNode root,int depth){
        if(root == null){
            if(maxDepth < depth){
                maxDepth = depth;
            }
            return;
        }
        TreeDepthMax(root.left,depth + 1);
        TreeDepthMax(root.right,depth + 1);
    }
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        int depth = 0;
        TreeDepthMax(root,depth);
        return maxDepth;
    }
}

解法三:求二叉树的层数遍历的遍历的层数

import java.util.*;
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        // 保存二叉树的层数
        int depth = 0;
        while(!queue.isEmpty()){
            // 当前层的结点个数就是当前队列中的结点个数
            int size = queue.size();
            // 队列不为空就说明还存在下一层
            depth++;
            // 遍历当前层并将当前层所有节点的左右子树(如果存在的话)入队列
            for(int i = 0;i < size;i++){
                // 获取队头元素并删除
                TreeNode tmp = queue.poll();
                if(tmp.left != null){
                    queue.offer(tmp.left);
                }
                if(tmp.right != null){
                    queue.offer(tmp.right);
                }
            }
        }
        return depth;
    }
}

30.数组中只出现一次的数字

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if(array == null || num1 == null || num2 == null){
            return;
        }
        num1[0] = 0;
        num2[0] = 0;
        // 对所有的数据进行异或,异或的结果就是只出现一次的两个数据的结果
        int result = array[0];
        for(int i = 1;i < array.length;i++){
            result ^= array[i];
        }
        // 求出int所比特位
        int length = Integer.SIZE;
        // 找到对所有数据异或后的二进制数据的第一个比特位为1的位置
        int flg = 1;
        while(length > 0){
            length -= 1;
            if(((flg<<length) & result) != 0){
                flg <<= length;
                break;
            }
        }
        // 由于异或后的数据比特位上为1,则异或前的两个数据该比特位上一定是一个为1,一个为0
        // 分组,再次遍历数组,将所有数据进行分组,该比特位上为1的分为一组,并将该组数据全部进行异或,得到只出现一次,且该比特位为1的数据;该比特位上为0的分为一组,并将该组数据全部进行异或,得到只出现一次,且该比特位为0的数据
        for(int i = 0;i < array.length;i++){
            if((flg & array[i]) == 0){
                num1[0] ^= array[i];
            }else{
                num2[0] ^= array[i];
            }
        }
    }
}

31.和为s的连续正数序列

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        int low = 1;
        int high = 2;
        while(low < high){
            // 求和公式:(a0+an)*n/2
            int total = (low + high) * (high - low + 1)/2;
            //如果当前和等于要求的和,将当前序列添加到list中,并将list添加到结果集当中,最后让low向后走一步
            if(total == sum){
                ArrayList<Integer> list = new ArrayList<>();
                for(int i = low;i <= high;i++){
                    list.add(i);
                }
                result.add(list);
                low++;
            }else if(total < sum){
                // 如果当前和小于要求的和,就让high向后走一步
                high++;
            }else{
                // 如果当前和小于要求的和,就让low向后走一步
                low++;
            }
        }
        return result;
    }
}

32.左旋转字符串

解法一:

public class Solution {
    // 逆置函数
    public void reverse(char[] ch,int start,int end){
        while(start < end){
            char tmp = ch[start];
            ch[start] = ch[end];
            ch[end] = tmp;
            start++;
            end--;
        }
    }
    public String LeftRotateString(String str,int n) {
        if(str == null || str.length() == 0){
            return new String();
        }
        int step = n % str.length();
        char[] ch = str.toCharArray();
        // 将起始位置到要左旋次数的字符进行逆置
        reverse(ch,0,step - 1);
        // 将要左旋次数的字符到字符串结尾的位置进行逆置
        reverse(ch,step,str.length() - 1);
        // 对整体进行逆置
        reverse(ch,0,str.length() - 1);
        return new String(ch);
    }
}

解法二:

public class Solution {
    // 向左移动一次
    public void LeftRotateStringHelp(char[] ch){
        // 保存第一个字符
        char tmp = ch[0];
        // 整体左移动一个元素
        for(int i = 0;i < ch.length - 1;i++){
            ch[i] = ch[i + 1];
        }
        // 将最后一个位置的字符变为第一个位置的字符
        ch[ch.length - 1] = tmp;
    }
    public String LeftRotateString(String str,int n) {
        if(str == null || str.length() == 0){
            return str;
        }
        // 保证没有重复左移
        int step = n % str.length();
        char[] ch = str.toCharArray();
        // 向左移动要移动的位数
        while(step > 0){
            LeftRotateStringHelp(ch);
            step--;
        }
        return new String(ch);
    }
}

33.翻转单词序列

public class Solution {
    // 逆置函数
    public void reverse(char[] ch,int start,int end){
        while(start < end){
            char tmp = ch[start];
            ch[start] = ch[end];
            ch[end] = tmp;
            start++;
            end--;
        }
    }
    public String ReverseSentence(String str) {
        if(str == null || str.length() == 0){
            return str;
        }
        char[] ch = str.toCharArray();
        // 先让整体进行逆置
        reverse(ch,0,ch.length - 1);
        int i = 0;
        while(i < ch.length){
            int j = i;
            // 这里的j指向每个单词的最后一个字母后面的空格,i指向每个单词的第一个字母
            while(j < ch.length && ch[j] != ' '){
                j++;
            }
            // 将每个单词进行逆置
            if(j < ch.length){
                reverse(ch,i,j - 1);
                i = j + 1;
            }else{
                // 逆置最后一个单词
                reverse(ch,i,j - 1);
                i = j;
            }
        }
        return new String(ch);
    }
}

34.按之字形顺序打印二叉树

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        // 保存最终的结果集
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        if(pRoot == null){
            return result;
        }
        // 
        Queue<TreeNode> queue = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.push(pRoot);
        boolean flg = true;//true:说明当前是应该从左往右遍历;false:说明当前应该是从右向左遍历
        while(!stack.empty()){
            ArrayList<Integer> list = new ArrayList<>();//保存一行的结果
            // 清空并让当前栈中的元素进入list中,将当前栈中每一个结点的左右子树入队列(如果存在的话)
            while(!stack.empty()){
                TreeNode tmp = stack.pop();
                list.add(tmp.val);
                // 判断当前是应该从左向右遍历还是从右向左遍历
                TreeNode left = (flg == true) ? tmp.left : tmp.right;
                TreeNode right = (flg == true) ? tmp.right : tmp.left;
                if(left != null){
                    queue.offer(left);
                }
                if(right != null){
                    queue.offer(right);
                }
            }
            // 将当前行的元素添加到最终的结果集中(注意深浅拷贝的问题)
            result.add(new ArrayList<>(list));
            // 将当前队列中的元素入栈
            while(!queue.isEmpty()){
                stack.push(queue.poll());
            }
            // 更改遍历的方向
            if(flg){
                flg = false;
            }else{
                flg = true;
            }
        }
        return result;
    }
}

35. 二叉搜索树的第k个节点

import java.util.*;
public class Solution {
    public int KthNode (TreeNode proot, int k) {
        if(proot == null || k <= 0){
           return -1; 
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(proot);
        TreeNode node = proot.left;
        while(node != null || !stack.empty()){
            // 让当前结点的左树全部入栈
            while(node != null){
                stack.push(node);
                node = node.left;
            }
            // 栈不为空值,弹出结点并判断有没有要找的倒数第k个结点,并让当前结点指向其右树
            if(!stack.empty()){
                node = stack.pop();
                k--;
                if(k == 0){
                    return node.val;
                }
                node = node.right;
            }
        }
        return -1;
    }
}

你可能感兴趣的:(每日一题,算法,数据结构,java)