算法训练--数组

290 阅读11分钟

[TOC]

算法训练--数组

数组基本理论

  • 数组(Array)是有序的元素序列,数组是存放在连续内存空间上的相同类型数据的集合,数组可以方便的通过下标索引的方式获取到下标下对应的数据

  • 正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址

    • 数组下标都是从0开始的
    • 数组内存空间的地址是连续的
  • 数组的元素是不能删的,只能覆盖

相关题目练习

283. 移动零

  • 题目描述

    image.png

  • 题解

    class Solution {
        public void moveZeroes(int[] nums) {
            //记录非零元素下标
            int i=0;
            for(int j=0;j<nums.length;j++){
                if(nums[j]!=0){
                    nums[i]=nums[j];
                    if(j!=i){
                        nums[j]=0;
                    }
                    i++;
                }
            }
        }
    }
    

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

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int removeDuplicates(int[] nums) {
            if(nums==null){
                return 0;
            }
            int i=1;
            for(int j=1;j<nums.length;j++){
                if(nums[j]!=nums[j-1]){
                    nums[i]=nums[j];
                    i++;
                }
            }
            return i;
        }
    }
    

70. 爬楼梯

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int climbStairs(int n) {
            if(n<=2){
                return n;
            }
            int f1=1,f2=2,f3=3;
            for(int i=3;i<n+1;i++){
                f3=f2+f1;
                f1=f2;
                f2=f3;
            }
            return f3;
            // if(n<=1) return n;
            // int[] nums=new int[n+1];
            // nums[1]=1;nums[2]=2;
            // for(int i=3;i<n+1;i++){
            //     nums[i]=nums[i-1]+nums[i-2];
            // }
            // return nums[n];
        }
    }
    

15. 三数之和

  • 题目描述

    image.png

  • 题解

    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> res=new ArrayList();
            if(nums==null){
                return res;
            }
            //先排序
            Arrays.sort(nums);
            for(int i=0;i<nums.length;i++){
                int j=i+1;
                int k=nums.length-1;
             		//去除肯定不成立分支
                if(nums[i]>0) break;
              	//过滤相同值
                if(i>0 && nums[i]==nums[i-1]) continue;
                while(j<k){
                    int sum=nums[i]+nums[j]+nums[k];
                    if(sum==0){
                        res.add(Arrays.asList(nums[i],nums[j],nums[k]));
                        while(j<k && nums[j]==nums[j+1]) j++;
                        while(j<k && nums[k]==nums[k-1]) k--;
                        j++;
                        k--;
                    }else if(sum>0){
                        k--;
                    }else{
                        j++;
                    }
                }
            }
            return res;
        }
    }
    

11. 盛最多水的容器

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int maxArea(int[] height) {
            //双指针
            int i=0,j=height.length-1,area=0;
            while(i<j){
                int h=Math.min(height[i],height[j]);
                area=Math.max(area,(j-i)*h);
                if(height[i]>height[j]){
                    j--;
                }else{
                    i++;
                }
            }
            return area;
        }
    }
    

844. 比较含退格的字符串

  • 题目描述

    image.png

  • 题解

    class Solution {
        public boolean backspaceCompare(String s, String t) {
            int sSkipNum=0;  //记录s的#数量
            int tSkipNum=0;  //记录t的#数量
            int i=s.length()-1;
            int j=t.length()-1;
            while(true){
                while(i>=0){
                    //从后往前,消除s的#
                    if(s.charAt(i)=='#') sSkipNum++;
                    else{
                        if(sSkipNum>0){
                            sSkipNum--;
                        }else{
                          //不存在#就跳出循环
                            break;
                        }
                    }
                    i--;
                }
                while(j>=0){
                    //从后往前,消除t的#
                    if(t.charAt(j)=='#'){
                        tSkipNum++;
                    }else{
                        if(tSkipNum>0){
                            tSkipNum--;
                        }else{
                            break;
                        }
                    }
                    j--;
                }
                //后半部分#消除完了,接下来比较
                if(i<0 || j<0) break;//s或t遍历到头了
                if(s.charAt(i)!=t.charAt(j)){
                    return false;
                }
                i--;j--;
            }
            if(i==-1 && j==-1){
                //同时遍历完成
                return true;
            }else{
                return false;
            }
        }
    }
    

977. 有序数组的平方

  • 题目描述

    image.png

  • 题解

    /**
        数组其实是有序的, 只不过负数平方之后可能成为最大数了
        那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间
     */
    class Solution {
        public int[] sortedSquares(int[] nums) {
            int r=nums.length-1;
            int l=0;
            int[] res=new int[nums.length];
            int index=nums.length-1;
            while(l<=r){
                if(nums[l]*nums[l]<nums[r]*nums[r]){
                    res[index--]=nums[r]*nums[r];
                    r--;
                }else{
                    res[index--]=nums[l]*nums[l];
                    l++;
                }
            }
            return res;
        }
    }
    

189. 轮转数组

  • 题目描述

    image.png

  • 题解

    /**
        三次反转,常考题
     */
    class Solution {
        public void rotate(int[] nums, int k) {
            int len=nums.length;
            //注意:k取模len
            k%=len;
            //整体反转 [7,6,5,4,3,2,1]
            reverse(nums,0,len-1);
            //再次反转前k个数据 [5,6,7,4,3,2,1]
            reverse(nums,0,k-1);
            //再次反转后len-k个数据 [5,6,7,1,2,3,4]
            reverse(nums,k,len-1);
        }
        public void reverse(int[] nums,int start,int end){
            while(start<end){
                int temp=nums[end];
                nums[end]=nums[start];
                nums[start]=temp;
                start++;
                end--;
            }
        }
    }
    

88. 合并两个有序数组

  • 题目描述

    image.png

  • 题解

    利用num1、num2已结排好序这一性质,我们可以使用双指针方法。这一方法将两个数组看作队列,每次从两个数组头部取出比较小的数字放到结果中。如下面的动画所示

    gif1

    class Solution {
        public void merge(int[] nums1, int m, int[] nums2, int n) {
            int p1=0,p2=0,curr=0;
            int[] sorted=new int[m+n];
            while(p1<m || p2<n){
                if(p1==m){
                    curr=nums2[p2++];
                }else if(p2==n){
                    curr=nums1[p1++];
                }else if(nums1[p1]<nums2[p2]){
                    curr=nums1[p1++];
                }else{
                    curr=nums2[p2++];
                }
                sorted[p1+p2-1]=curr;
            }
            for(int i=0;i<m+n;i++){
                nums1[i]=sorted[i];
            }
        }
    }
    

66. 加一

  • 题目描述

    image.png

  • 题解

    /**
    	当我们对数组digits加一时,我们只需要关注digits的末尾出现了多少个9即可
    	只需要对数组digits进行一次逆序遍历,找出第一个不为9的元素,将其加一并将后续所有元素置零即可
    	如果digits中所有的元素均为9,我们需要返回一个新的数组
    */
    class Solution {
        public int[] plusOne(int[] digits) {
            for(int i=digits.length-1;i>=0;i--){
                digits[i]=(digits[i]+1)%10;
                if(digits[i]!=0){
                    return digits;
                }
            }
            int[] res=new int[digits.length+1];
            res[0]=1;
            return res;
        }
    }
    

二分查找

  • 这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了

704. 二分查找

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int search(int[] nums, int target) {
            int left=0,right=nums.length-1;
            //left==right 因此mid-1或者mid+1
            while(left<=right){
                int mid=(right-left)/2+left;
                if(nums[mid]==target){
                    return mid;
                }else if(nums[mid]>target){
                    right=mid-1;
                }else{
                    left=mid+1;
                }
            }
             return -1;
        }
    }
    

35. 搜索插入位置

  • 题目描述

    image.png

  • 题解

    image.png

    class Solution {
        public int searchInsert(int[] nums, int target) {
            int n=nums.length;
            int left=0,right=n-1,ans=n;
            while(left<=right){
                int mid=(right-left)/2+left;
                if(target<=nums[mid]){
                    ans=mid;
                    right=mid-1;
                }else{
                    left=mid+1;
                }
            }
            return ans;
        }
    }
    

69. x 的平方根 (*)

  • 题目描述

    image.png

  • 题解

    /**
        找到某个值的平方等于或者没找到则返回最大的一个平方小于x的即退出循环的right
     */
    class Solution {
        public int mySqrt(int x) {
            int left=1,right=x;
            while(left<=right){
                int mid=(right-left)/2+left;
                //用x/m<m而不是m*m>x防止整数溢出
                if(mid>x/mid){
                    right=mid-1;
                }else if(mid<x/mid){
                    left=mid+1;
                }else{
                    return mid;
                }
            }
            return right;
        }
    }
    
  • 变种:二分法 求浮点数的平方根 保留n位小数

    public class Solution {
      	//1乘10的负15次方
        private static double epsilon = 1e-15;
        public static void main(String[] args){
            double ans = mySqrt(x,epsilon);
          	//精确到n为小数输出
            System.out.printf(String.format("%.3f",ans));
        }
    
        public static double mySqrt(double x , double epsilon){
            double left = 0 , right = x;
            if(x == 0 || x == 1){
                return x;
            }
            while(left <= right){
                double mid = left + (right - left) / 2;
              	//绝对值小于 epsilon
                if(Math.abs(mid * mid - x) < epsilon){
                    return mid;
                }else if(mid * mid < x){
                    left = mid+1;
                }else{
                    right = mid-1;
                }
            }
            return right;
        }
    }
    

367. 有效的完全平方数

  • 题目描述

    image.png

  • 题解

    class Solution {
        public boolean isPerfectSquare(int num) {
            int left=1,right=num;
            while(left<=right){
                int mid=(right-left)/2+left;
                long square=(long)mid*mid;
                if(num<square){
                    right=mid-1;
                }else if(num>square){
                    left=mid+1;
                }else{
                    return true;
                }
            }
            return false;
        }
    }
    

33. 搜索旋转排序数组

  • 题目描述

    image.png

  • 题解

    class Solution {
        public 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,right=nums.length-1;
            while(left<=right){
                int mid=(left+right)/2;
                if(nums[mid]==target) return mid;
                else if(nums[mid]<nums[right]){
                    if(nums[mid]<target && target<=nums[right]){
                        left=mid+1;
                    }else{
                        right=mid-1;
                    }
                }else{
                    if(nums[left]<=target && target<nums[mid]){
                        right=mid-1;
                    }else{
                        left=mid+1;
                    }
                }
            }
            return -1;
        }
    }
    

209. 长度最小的子数组

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int minSubArrayLen(int target, int[] nums) {
            int res=Integer.MAX_VALUE;
            int left=0;
            int sum=0;
            for(int right=left;right<nums.length;right++){
                sum+=nums[right];
                while(sum>=target){
                    res=Math.min(res,right-left+1);
                    sum-=nums[left];
                    left++;
                }
            }
            return res==Integer.MAX_VALUE?0:res;
        }
    }
    

4. 寻找两个正序数组的中位数

  • 题目描述

    image.png

  • 题解

    class Solution {
           public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int length1 = nums1.length, length2 = nums2.length;
            int totalLength = length1 + length2;
            if (totalLength % 2 == 1) {
                int midIndex = totalLength / 2;
                double median = getKthElement(nums1, nums2, midIndex + 1);
                return median;
            } else {
                int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
                double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
                return median;
            }
        }
    
        public int getKthElement(int[] nums1, int[] nums2, int k) {
            int length1 = nums1.length, length2 = nums2.length;
            int index1 = 0, index2 = 0;
            int kthElement = 0;
    
            while (true) {
                // 边界情况
                if (index1 == length1) {
                    return nums2[index2 + k - 1];
                }
                if (index2 == length2) {
                    return nums1[index1 + k - 1];
                }
                if (k == 1) {
                    return Math.min(nums1[index1], nums2[index2]);
                }
                
                // 正常情况
                int half = k / 2;
                int newIndex1 = Math.min(index1 + half, length1) - 1;
                int newIndex2 = Math.min(index2 + half, length2) - 1;
                int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
                if (pivot1 <= pivot2) {
                    k -= (newIndex1 - index1 + 1);
                    index1 = newIndex1 + 1;
                } else {
                    k -= (newIndex2 - index2 + 1);
                    index2 = newIndex2 + 1;
                }
            }
        }
    }
    

162. 寻找峰值

  • 题目描述

    image.png

  • 题解

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

74. 搜索二维矩阵

  • 题目描述

    image.png

  • 题解

    若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。

    代码实现时,可以二分升序数组的下标,将其映射到原矩阵的行和列上

    class Solution {
        public boolean searchMatrix(int[][] matrix, int target) {
            int m = matrix.length, n = matrix[0].length;
            int low = 0, high = m * n - 1;
            while (low <= high) {
                int mid = (high - low) / 2 + low;
                int x = matrix[mid / n][mid % n];
                if (x < target) {
                    low = mid + 1;
                } else if (x > target) {
                    high = mid - 1;
                } else {
                    return true;
                }
            }
            return false;
        }
    }
    

240. 搜索二维矩阵 II

  • 题目描述

    image.png

  • 题解

    image.png

    class Solution {
        public boolean searchMatrix(int[][] matrix, int target) {
            int m = matrix.length, n = matrix[0].length;
            int x = 0, y = n - 1;
            while (x < m && y >= 0) {
                if (matrix[x][y] == target) {
                    return true;
                }
                if (matrix[x][y] > target) {
                    --y;
                } else {
                    ++x;
                }
            }
            return false;
        }
    }
    

34. 在排序数组中查找元素的第一个和最后一个位置

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int[] searchRange(int[] nums, int target) {
            int[] res=new int[]{-1,-1};
            if(nums.length==0) return res;
            res[0]=searchBorder(nums,target,true);
            res[1]=searchBorder(nums,target,false);
            return res;
        }
        public int searchBorder(int[] nums,int target,boolean leftOrRight){
            int temp=-1;
            int left=0,right=nums.length-1;
            while(left<=right){
                int mid=(left+right)/2;
                if(nums[mid]<target){
                    left=mid+1;
                }else if(nums[mid]>target){
                    right=mid-1;
                }else{
                    temp=mid;
                    if(leftOrRight){
                        right=mid-1;
                    }else{
                        left=mid+1;
                    }
                }
            }
            return temp;
        }
    }
    

50. Pow(x, n)

  • 题目描述

    image.png

  • 题解

    class Solution {
        public double myPow(double x, int n) {
            double res=1.0;
            for(int i=n;i!=0;i/=2){
                if(i%2!=0){
                    res*=x;
                }
                x*=x;
            }
            return n<0?1/res:res;
        }
    }
    

287. 寻找重复数

  • 题目描述

    image.png

  • 题解

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

153. 寻找旋转排序数组中的最小值

  • 题目描述

    image.png

  • 题解

    前面一堆0,后面一堆1,然后寻找第一个1的二分问题

    class Solution {
        public int findMin(int[] nums) {
            int low = 0;
            int high = nums.length - 1;
            while (low <high) {
                int mid = low + (high - low) / 2;
                if (nums[mid] < nums[high]) {
                    high = mid;
                } else {
                    low = mid + 1;
                }
            }
            return nums[low];
        }
    }
    

滑动窗口

  • 所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

209. 长度最小的子数组

  • 题目描述

    image.png

  • 题解

    这里以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:

    其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动

    主要确定如下三点:

    • 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组
    • 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)
    • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了
    class Solution {
        public int minSubArrayLen(int target, int[] nums) {
            //滑动窗口 left窗口起始位置 right窗口结束位置
            int sum=0,left=0,result=Integer.MAX_VALUE;
            for(int right=0;right<nums.length;right++){
                sum+=nums[right];
                while(sum>=target){
                    result=Math.min(result,right-left+1);
                    //减去left位的值,并且left后移一位
                    sum-=nums[left++];
                }
            }
            return result==Integer.MAX_VALUE?0:result;
        }
    }
    

904. 水果成篮

  • 题目描述

    image.png

  • 题解

    /**
        最长的包含两种不同“类型”的子序列
     */
    class Solution {
        public int totalFruit(int[] fruits) {
            //两个篮子==两种水果类型
            int res=0,left=0;
            // key:水果类型 value:水果数量
            Map<Integer,Integer> map=new HashMap();
            for(int right=0;right<fruits.length;right++){
                map.put(fruits[right],map.getOrDefault(fruits[right],0)+1);
                while(map.size()>2){
                    //超过两种类型,需要移除一种类型,通过移动left指针
                    map.put(fruits[left],map.get(fruits[left])-1);
                    if(map.get(fruits[left])==0){
                        map.remove(fruits[left]);
                    }
                    left++;
                }
                res=Math.max(res,right-left+1);
            }
            return res;
        }
    }
    

76. 最小覆盖子串

  • 题目描述

    image.png

  • 题解

    class Solution {
        public String minWindow(String s, String t) {
            int len=s.length(),left=0,right=0,vailded=0;
            int min=Integer.MAX_VALUE,start=0;
            Map<Character,Integer> need=new HashMap();
            Map<Character,Integer> window=new HashMap();
            char[] tArr=t.toCharArray();
            char[] sArr=s.toCharArray();
            for(char ch:tArr){
                need.put(ch,need.getOrDefault(ch,0)+1);
            }
            while(right<len){
            //for(int right=0;right<len;right++){
                //更新窗口状态
                char cright=sArr[right];
                right++;
                if(need.containsKey(cright)){
                     window.put(cright,window.getOrDefault(cright,0)+1);
                     if(need.get(cright).equals(window.get(cright))) vailded++;
                }
                //判断左侧窗口是否要收缩
                while(vailded==need.size()){
                    //更新长度最小的子串
                    if(right-left<min){
                        min=right-left;
                        start=left;
                    }
                    //要移除的字符
                    char cleft=sArr[left];
                    left++;
                    //更新滑动窗口
                    if(need.containsKey(cleft)){
                        //取消满足条件
                        if(need.get(cleft).equals(window.get(cleft))) vailded--;
                        window.put(cleft,window.getOrDefault(cleft,0)-1);
                    }
                }
            }
            min=min==Integer.MAX_VALUE?0:min;
            return s.substring(start,start+min);
        }
    }
    

螺旋矩阵

59. 螺旋矩阵 II

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int[][] generateMatrix(int n) {
            int[][] res=new int[n][n];
            int startX=0,startY=0;//定义每循环一个圈的起始位置
            int count=1;//用来给矩阵中每一个空格赋值
            int offset=1;//每一圈循环,需要控制每一条边遍历的长度
            int mid=n/2;//矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
            int loop=n/2;//每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
            while(loop>0){
                int i=startX;
                int j=startY;
                // 下面开始的四个for就是模拟转了一圈
                // 模拟填充上行从左到右(左闭右开)
                for(;j<startY+n-offset;j++){
                    res[i][j]=count++;
                }
                // 模拟填充右列从上到下
                for(;i<startX+n-offset;i++){
                    res[i][j]=count++;
                }
                // 模拟填充下行从右到左
                for(;j>startY;j--){
                    res[i][j]=count++;
                }
                // 模拟填充左列从下到上
                for(;i>startX;i--){
                    res[i][j]=count++;
                }
                loop--;
                // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
                startX++;
                startY++;
                // offset 控制每一圈里每一条边遍历的长度
                offset+=2;
            }
            // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
            if(n%2!=0){
                res[mid][mid]=count;
            }
            return res;
        }
    }
    

54. 螺旋矩阵

  • 题目描述

    image.png

    image.png

  • 题解

    class Solution {
        public List<Integer> spiralOrder(int[][] matrix) {
            int startX=0,stratY=0;
            int m=matrix.length,n=matrix[0].length;
            List<Integer> list=new ArrayList();
            int loop=Math.min(m,n)/2; //行列中选择小的进行循环
            int offset=1;
            while(loop>0){
                int i=startX;
                int j=stratY;
                //上边 从左到右
                for(;j<stratY+n-offset;j++){
                    list.add(matrix[i][j]);
                }
                //右边 从上到下
                for(;i<startX+m-offset;i++){
                    list.add(matrix[i][j]);
                }
                // 下边 从右到左
                for(;j>stratY;j--){
                    list.add(matrix[i][j]);
                }
                // 左边 从下到上
                for(;i>startX;i--){
                    list.add(matrix[i][j]);
                }
                startX++;
                stratY++;
                offset+=2;
                loop--;
            }
            //行列相同,但为奇数需单独处理中间的值
            if(m==n && n%2!=0){
                list.add(matrix[m/2][n/2]);
            }
            //行大于列,且列为奇数
            if(m>n && n%2!=0){
                for(int i=n/2;i<m-n/2;i++){
                        list.add(matrix[i][n/2]);
                }
            }
            //列大于行,且行为奇数
            if(n>m && m%2!=0){
                for(int i=m/2;i<n-m/2;i++){
                    list.add(matrix[m/2][i]);
                }
            }
             return list;
        }
    }
    

剑指 Offer 29. 顺时针打印矩阵

  • 题目描述(此题同54)

    image.png

  • 题解

    class Solution {
        public int[] spiralOrder(int[][] matrix) {
            if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
                return new int[0];
            }
            int startX=0,stratY=0,offset=1;
            int m=matrix.length,n=matrix[0].length;
            int loop=Math.min(m,n)/2;
            int[] res=new int[m*n];
            int count=0;
            while(loop>0){
                int i=startX;
                int j=stratY;
                for(;j<stratY+n-offset;j++){
                    res[count++]=matrix[i][j];
                }
                for(;i<startX+m-offset;i++){
                    res[count++]=matrix[i][j];
                }
                for(;j>stratY;j--){
                    res[count++]=matrix[i][j];
                }
                for(;i>startX;i--){
                    res[count++]=matrix[i][j];
                }
                startX++;
                stratY++;
                offset+=2;
                loop--;
            }
            if(m==n && m%2!=0){
                res[count]=matrix[m/2][n/2];
            }
            if(m>n && n%2!=0){
                for(int i=n/2;i<m-n/2;i++){
                    res[count++]=matrix[i][n/2];
                }
            }
            if(n>m && m%2!=0){
                for(int i=m/2;i<n-m/2;i++){
                    res[count++]=matrix[m/2][i];
                }
            }
            return res;
        }
    }
    

CodeTop系列

数组

283. 移动零

  • 题目描述

    image.png

  • 题解

    class Solution {
        public void moveZeroes(int[] nums) {
            //记录非零元素
            int i=0;
            for(int j=0;j<nums.length;j++){
                if(nums[j]!=0){
                    nums[i]=nums[j];
                    if(i!=j){
                        nums[j]=0;
                    }
                    i++;
                }
            }
        }
    }
    

560. 和为 K 的子数组

  • 题目描述

    image.png

  • 题解

    /**
    	哈希表
    */
    class Solution {
        public int subarraySum(int[] nums, int k) {
            //扫描一遍数组,使用map记录同样的和的次数
            //对每个i计算累加sum并判断map内是否有sum-k
            Map<Integer,Integer> map=new HashMap<>();
            map.put(0,1);
            int sum=0,res=0;
            for(int i=0;i<nums.length;i++){
                sum+=nums[i];
                if(map.containsKey(sum-k)){
                    res+=map.get(sum-k);
                }
                map.put(sum,map.getOrDefault(sum,0)+1);
            }
            return res;
        }
    }
    

42. 接雨水

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int trap(int[] height) {
            Deque<Integer> stack=new LinkedList<>();
            int area=0;
            for(int i=0;i<height.length;i++){
                while(!stack.isEmpty() && height[stack.peek()]<height[i]){
                    int preIndex=stack.pop();
                    if(!stack.isEmpty()){
                        int h=Math.min(height[i],height[stack.peek()])-height[preIndex];
                        int w=i-stack.peek()-1;
                        area+=w*h;
                    }
                }
                stack.push(i);
            }
            return area;
        }
    }
    

4. 寻找两个正序数组的中位数

  • 题目描述

    image.png

  • 题解

    class Solution {
           public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int length1 = nums1.length, length2 = nums2.length;
            int totalLength = length1 + length2;
            if (totalLength % 2 == 1) {
                int midIndex = totalLength / 2;
                double median = getKthElement(nums1, nums2, midIndex + 1);
                return median;
            } else {
                int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
                double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
                return median;
            }
        }
    
        public int getKthElement(int[] nums1, int[] nums2, int k) {
            int length1 = nums1.length, length2 = nums2.length;
            int index1 = 0, index2 = 0;
            int kthElement = 0;
    
            while (true) {
                // 边界情况
                if (index1 == length1) {
                    return nums2[index2 + k - 1];
                }
                if (index2 == length2) {
                    return nums1[index1 + k - 1];
                }
                if (k == 1) {
                    return Math.min(nums1[index1], nums2[index2]);
                }
                
                // 正常情况
                int half = k / 2;
                int newIndex1 = Math.min(index1 + half, length1) - 1;
                int newIndex2 = Math.min(index2 + half, length2) - 1;
                int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
                if (pivot1 <= pivot2) {
                    k -= (newIndex1 - index1 + 1);
                    index1 = newIndex1 + 1;
                } else {
                    k -= (newIndex2 - index2 + 1);
                    index2 = newIndex2 + 1;
                }
            }
        }
    }
    

162. 寻找峰值

  • 题目描述

    image.png

  • 题解

    O(logN)一般考虑二分搜索。有如下规律:

    规律一:如果nums[i] > nums[i+1],则在i之前一定存在峰值元素

    规律二:如果nums[i] < nums[i+1],则在i+1之后一定存在峰值元素

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

78. 子集

  • 题目描述

    image.png

  • 题解

    class Solution {
        List<List<Integer>> res=new ArrayList<>();
        List<Integer> temp=new ArrayList<>();
        public List<List<Integer>> subsets(int[] nums) {
            backTracking(nums,0);
            return res;
        }
        public void backTracking(int[] nums,int index){
            //收集子集,要放在终止添加的上面,否则会漏掉自己
            res.add(new ArrayList(temp));
            if(index>=nums.length){
                return;
            }
            for(int i=index;i<nums.length;i++){
                temp.add(nums[i]);
                backTracking(nums,i+1);
                temp.remove(temp.size()-1);
            }
        }
    }
    

11. 盛最多水的容器

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int maxArea(int[] height) {
            int  max=0;
            int left=0,right=height.length-1;
            while(left<right){
                int w=right-left;
                int h=Math.min(height[left],height[right]);
                max=Math.max(max,h*w);
                if(height[left]>height[right]){
                    right--;
                }else{
                    left++;
                }
            }
            return max;
        }
    }
    

79. 单词搜索

  • 题目描述

    image.png

  • 题解

    /**
    * 回溯法:相比于DFS,多了一步『撤销修改节点状态』
    */
    class Solution {
        //定义为成员变量,方便以下两个成员方法使用和修改
        private boolean find; 
        public boolean exist(char[][] board, String word) {
            if (board == null) return false;
            int m = board.length, n = board[0].length;
            boolean[][] visited = new boolean[m][n];
            find = false;
    
            for (int i = 0; i < m; i++){
                for (int j = 0; j < n; j++){
                  	//从左上角开始遍历棋盘每个格子
                    backtracking(i, j, board, word, visited, 0);  
                }
            }
            return find;
        }
    
        /**
        * i,j,board:棋盘格及当前元素的坐标
        * word: 要搜索的目标单词
        * visited:记录当前格子是否已被访问过
        * pos: 记录目标单词的字符索引,只有棋盘格字符和pos指向的字符一致时,
        * 才有机会继续搜索接下来的字符;如果pos已经过了目标单词的尾部了,那么便说明找到目标单词了
        */
        public void backtracking(int i, int j, char[][] board, String word, boolean[][] visited, int pos){
            // 超出边界、已经访问过、已找到目标单词、棋盘格中当前字符已经和目标字符不一致了
            if(i<0 || i>= board.length || j<0 || j >= board[0].length || visited[i][j] || find
               || board[i][j]!=word.charAt(pos))  return;
    
            if(pos == word.length()-1){
                find = true;
                return;
            }
          	//修改当前节点状态
            visited[i][j] = true;
          	//遍历子节点
            backtracking(i+1, j, board, word, visited, pos+1);
            backtracking(i-1, j, board, word, visited, pos+1);
            backtracking(i, j+1, board, word, visited, pos+1);
            backtracking(i, j-1, board, word, visited, pos+1);
          	//撤销修改
            visited[i][j] = false;
        }
    
    }
    

64. 最小路径和

  • 题目描述

    image.png

  • 题解

    image.png

    class Solution {
        public int minPathSum(int[][] grid) {
            if (grid == null || grid.length == 0 || grid[0].length == 0) {
                return 0;
            }
            int rows = grid.length, columns = grid[0].length;
            int[][] dp = new int[rows][columns];
            dp[0][0] = grid[0][0];
            for (int i = 1; i < rows; i++) {
                dp[i][0] = dp[i - 1][0] + grid[i][0];
            }
            for (int j = 1; j < columns; j++) {
                dp[0][j] = dp[0][j - 1] + grid[0][j];
            }
            for (int i = 1; i < rows; i++) {
                for (int j = 1; j < columns; j++) {
                    dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
                }
            }
            return dp[rows - 1][columns - 1];
        }
    }
    

双指针

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

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int lengthOfLongestSubstring(String s) {
            if(s.length()==0) return 0;
            Map<Character,Integer> map=new HashMap<>();
            int left=0,max=Integer.MIN_VALUE;
            for(int right=0;right<s.length();right++){
                char cRight=s.charAt(right);
                map.put(cRight,map.getOrDefault(cRight,0)+1);
                while(map.get(cRight)>1){
                    //出现重复
                    char cLeft=s.charAt(left);
                    map.put(cLeft,map.get(cLeft)-1);
                    left++;
                }
                max=Math.max(max,right-left+1);
            }
            return max;
        }
    }
    

141. 环形链表

  • 题目描述

    image.png

  • 题解

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

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

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode fast=dummy;
            ListNode slow=dummy;
            while(n>=0){
                fast=fast.next;
                n--;
            }
            while(fast!=null){
                fast=fast.next;
                slow=slow.next;
            }
            slow.next=slow.next.next;
            return dummy.next;
        }
    }
    

209. 长度最小的子数组

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int minSubArrayLen(int target, int[] nums) {
            int res=Integer.MAX_VALUE;
            int left=0;
            int sum=0;
            for(int right=left;right<nums.length;right++){
                sum+=nums[right];
                while(sum>=target){
                    res=Math.min(res,right-left+1);
                    sum-=nums[left];
                    left++;
                }
            }
            return res==Integer.MAX_VALUE?0:res;
        }
    }
    

88. 合并两个有序数组

  • 题目描述

    image.png

  • 题解

    class Solution {
        public void merge(int[] nums1, int m, int[] nums2, int n) {
            int p1=0,p2=0,cur=0;
            int[] sorted=new int[m+n];
            while(p1<m || p2<n){
                if(p1==m){
                    cur=nums2[p2++];
                }else if(p2==n){
                    cur=nums1[p1++];
                }else if(nums1[p1]<nums2[p2]){
                    cur=nums1[p1++];
                }else{
                    cur=nums2[p2++];
                }
                sorted[p1+p2-1]=cur;
            }
            for(int i=0;i<sorted.length;i++){
                nums1[i]=sorted[i];
            }
        }
    }
    

76. 最小覆盖子串

  • 题目描述

    image.png

  • 题解

    class Solution {
        public String minWindow(String s, String t) {
            if(s.length()==0) return "";
            if(t.length()>s.length()) return "";
            char[] sArr=s.toCharArray();
            char[] tArr=t.toCharArray();
            Map<Character,Integer> needMap=new HashMap<>();
            Map<Character,Integer> map=new HashMap<>();
            int left=0,maxNum=0,start=0,minLen=Integer.MAX_VALUE;
            for(char c:tArr){
                needMap.put(c,needMap.getOrDefault(c,0)+1);
            }
            for(int right=0;right<sArr.length;right++){
                char ch=sArr[right];
                map.put(ch,map.getOrDefault(ch,0)+1);
                if(map.get(ch).equals(needMap.get(ch))){
                    //满足条件的字符数
                    maxNum++;
                }
                //满足条件时缩小窗口
                while(needMap.size()==maxNum){
                    //记录当前窗口
                    if(right-left<minLen){
                        start=left;
                        minLen=right-left+1;
                    }
                    //缩小左边界
                    char cleft=sArr[left];
                    if(map.get(cleft).equals(needMap.get(cleft))){
                        maxNum--;
                    }
                    map.put(cleft,map.get(cleft)-1);
                    left++;
                }
            }
    		return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
        }
    }
    

15. 三数之和

  • 题目描述

    image.png

  • 题解

    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> res=new ArrayList<>();
            if(nums.length==0) return res;
            Arrays.sort(nums);
            for(int i=0;i<nums.length;i++){
                if(nums[i]>0) break;
                if(i>0 && nums[i]==nums[i-1]) continue;
                int j=i+1,k=nums.length-1;
                while(j<k){
                    int sum=nums[i]+nums[j]+nums[k];
                    if(sum==0){
                        res.add(Arrays.asList(nums[i],nums[j],nums[k]));
                        while(j<k && nums[j]==nums[j+1]) j++;
                        while(j<k && nums[k]==nums[k-1]) k--;
                        j++;
                        k--;
                    }else if(sum>0){
                        k--;
                    }else{
                        j++;
                    }
                }
            }
            return res;
        }
    }
    

11. 盛最多水的容器

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int maxArea(int[] height) {
            int  max=0;
            int left=0,right=height.length-1;
            while(left<right){
                int w=right-left;
                int h=Math.min(height[left],height[right]);
                max=Math.max(max,h*w);
                if(height[left]>height[right]){
                    right--;
                }else{
                    left++;
                }
            }
            return max;
        }
    }
    

42. 接雨水

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int trap(int[] height) {
            Deque<Integer> stack=new LinkedList<>();
            int area=0;
            for(int i=0;i<height.length;i++){
                while(!stack.isEmpty() && height[stack.peek()]<height[i]){
                    int preIndex=stack.pop();
                    if(!stack.isEmpty()){
                        int h=Math.min(height[i],height[stack.peek()])-height[preIndex];
                        int w=i-stack.peek()-1;
                        area+=w*h;
                    }
                }
                stack.push(i);
            }
            return area;
        }
    }
    

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

  • 题目描述

    image.png

    image.png

  • 题解

    class Solution {
        public int removeDuplicates(int[] nums) {
            int i=1;
            for(int j=1;j<nums.length;j++){
                if(nums[j]!=nums[j-1]){
                    nums[i]=nums[j];
                    i++;
                }
            }
            return i;
        }
    }
    

142. 环形链表 II

  • 题目描述

    image.png

  • 题解

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

234. 回文链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public boolean isPalindrome(ListNode head) {
            //1、快慢指针找出链表中点
            if(head==null || head.next==null) return true;
            ListNode fast=head;
            ListNode slow=fast;
            while(fast!=null && fast.next!=null){
                fast=fast.next.next;
                slow=slow.next;
            }
            ListNode pre=null;
            if(fast!=null) slow=slow.next;  //偶数节点
            //2、反转后半部分
            while(slow!=null){
                ListNode temp=slow.next;
                slow.next=pre;
                pre=slow;
                slow=temp;
            }
            //3、比较从头结点到中间节点是否相同
            while(head!=null && pre !=null){
                if(head.val!=pre.val) return false;
                head=head.next;
                pre=pre.next;
            }
            return true;
        }
    }
    

61. 旋转链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode rotateRight(ListNode head, int k) {
            if (head == null || head.next == null || k == 0) return head;
            int count = 1; // 用来统计链表总结点数
            ListNode tmp = head;
            while (tmp.next != null) {
                count++;
                tmp = tmp.next;
            }   
            k %= count;
            // 当k为0时,不需要旋转,
            if (k == 0) return head;
            // 不满足上述条件,必将进行旋转,所以先将首尾相连
            tmp.next = head;
            // 现在只需要找到倒数第k+1个节点
            for (int i = 0; i < count - k; i++) {
                tmp = tmp.next;
            }
            ListNode newHead = tmp.next;
            tmp.next = null;
            return newHead; 
        }
    }
    

排序

148. 排序链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        /**
         * 参考:Sort List——经典(链表中的归并排序) https://www.cnblogs.com/qiaozhoulin/p/4585401.html
         * 
         * 归并排序法:在动手之前一直觉得空间复杂度为常量不太可能,因为原来使用归并时,都是 O(N)的,
         * 需要复制出相等的空间来进行赋值归并。对于链表,实际上是可以实现常数空间占用的(链表的归并
         * 排序不需要额外的空间)。利用归并的思想,递归地将当前链表分为两段,然后merge,分两段的方
         * 法是使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后
         * 慢的所在位置就是中间位置,这样就分成了两段。merge时,把两段头部节点值比较,用一个 p 指向
         * 较小的,且记录第一个节点,然后 两段的头一步一步向后走,p也一直向后走,总是指向较小节点,
         * 直至其中一个头为NULL,处理剩下的元素。最后返回记录的头即可。
         * 
         * 主要考察3个知识点,
         * 知识点1:归并排序的整体思想
         * 知识点2:找到一个链表的中间节点的方法
         * 知识点3:合并两个已排好序的链表为一个新的有序链表
         */
        public ListNode sortList(ListNode head) {
            return head == null ? null : mergeSort(head);
        }
    
        private ListNode mergeSort(ListNode head) {
            if (head.next == null) {
                return head;
            }
            ListNode p = head, q = head, pre = null;
            while (q != null && q.next != null) {
                pre = p;
                p = p.next;
                q = q.next.next;
            }
            pre.next = null;
            ListNode l = mergeSort(head);
            ListNode r = mergeSort(p);
            return merge(l, r);
        }
    
        ListNode merge(ListNode l, ListNode r) {
            ListNode dummyHead = new ListNode(0);
            ListNode cur = dummyHead;
            while (l != null && r != null) {
                if (l.val <= r.val) {
                    cur.next = l;
                    cur = cur.next;
                    l = l.next;
                } else {
                    cur.next = r;
                    cur = cur.next;
                    r = r.next;
                }
            }
            if (l != null) {
                cur.next = l;
            }
            if (r != null) {
                cur.next = r;
            }
            return dummyHead.next;
        }
    }
    

912. 排序数组

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int[] sortArray(int[] nums) {
        if(nums.length <=1)return nums;
         //qSort(nums,0,nums.length-1);
            //selectSort(nums);
            // insertSort(nums);
            // shellSort(nums);
            // bucketSort(nums);
            // countSort(nums);
            // mergeSort(nums,0,nums.length-1);
            heapSort(nums);
        return nums;
        }
    
        /**
        快速排序
        **/
        void qSort(int[] arr,int s,int e){
            int l = s, r = e;
            if(l < r){
                int temp = arr[l];
                while(l < r){
                    while(l < r && arr[r] >= temp) r--;
                    if(l < r) arr[l] = arr[r];
                    while(l < r && arr[l] < temp) l++;
                    if(l < r) arr[r] = arr[l];
                }
                arr[l] = temp;
                qSort(arr,s,l);
                qSort(arr,l + 1, e);
            }
        }
        /**
        选择排序
        **/
        void selectSort(int[] arr){
            int min;
            for(int i = 0;i<arr.length;i++){
                min = i;
                for(int j = i;j<arr.length;j++){
                    if(arr[j] < arr[min]){
                        min = j;
                    }
                }
                if(min != i) {
                    int temp = arr[i];
                    arr[i] = arr[min];
                    arr[min] = temp;
                }
            }
        }
        /**
         *
         * 插入排序:数列前面部分看为有序,依次将后面的无序数列元素插入到前面的有序数列中,
         初始状态有序数列仅有一个元素,即首元素。在将无序数列元素插入有序数列的过程中,
         采用了逆序遍历有序数列,相较于顺序遍历会稍显繁琐,但当数列本身已近排序状态效率会更高。
         *
         * 时间复杂度:O(N2)   稳定性:稳定
         * @param arr
         */
        public void insertSort(int arr[]){
            for(int i = 1; i < arr.length; i++){
                int rt = arr[i];
                for(int j = i - 1; j >= 0; j--){
                    if(rt < arr[j]){
                        arr[j + 1] = arr[j];
                        arr[j] = rt;
                    }else{
                        break;
                    }
                }
            }
        }
        /**
         * 希尔排序 - 插入排序的改进版
         为了减少数据的移动次数,在初始序列较大时取较大的步长,通常取序列长度的一半,
         此时只有两个元素比较,交换一次;
         之后步长依次减半直至步长为1,即为插入排序,由于此时序列已接近有序,
         故插入元素时数据移动的次数会相对较少,效率得到了提高。
         *
         * 时间复杂度:通常认为是O(N3/2) ,未验证  稳定性:不稳定
         * @param arr
         */
        void shellSort(int arr[]){
            int d = arr.length >> 1;
            while(d >= 1){
                for(int i = d; i < arr.length; i++){
                    int rt = arr[i];
                    for(int j = i - d; j >= 0; j -= d){
                        if(rt < arr[j]){
                            arr[j + d] = arr[j];
                            arr[j] = rt;
                        }else break;
                    }
                }
                d >>= 1;
            }
        }
        /**
         * 桶排序 - 实现线性排序,但当元素间值得大小有较大差距时会带来内存空间的较大浪费
         首先,找出待排序列中得最大元素max,申请内存大小为max + 1的桶(数组)并初始化为0;
         然后,遍历排序数列,并依次将每个元素作为下标的桶元素值自增1;
         最后,遍历桶元素,并依次将值非0的元素下标值载入排序数列(
         桶元素>1表明有值大小相等的元素,此时依次将他们载入排序数列),遍历完成,排序数列便为有序数列。
         *
         * 时间复杂度:O(x*N)   稳定性:稳定
         * @param arr
         */
         void bucketSort(int[] arr){
            int[] bk = new int[50000 * 2 + 1];
            for(int i = 0; i < arr.length; i++){
                bk[arr[i] + 50000] += 1;
            }
            int ar = 0;
            for(int i = 0; i < bk.length; i++){
                for(int j = bk[i]; j > 0; j--){
                    arr[ar++] = i - 50000;
                }
            }
        }
            /**
         * 基数排序 - 桶排序的改进版,桶的大小固定为10,减少了内存空间的开销。
         首先,找出待排序列中得最大元素max,并依次按max的低位到高位对所有元素排序;
         桶元素10个元素的大小即为待排序数列元素对应数值为相等元素的个数,
         即每次遍历待排序数列,桶将其按对应数值位大小分为了10个层级,
         桶内元素值得和为待排序数列元素个数。
         * @param arr
         */
         void countSort(int[] arr){
            int[] bk = new int[19];
            Integer max = Integer.MIN_VALUE;
            for(int i = 0; i < arr.length; i++){
                if(max < Math.abs(arr[i])) max = arr[i];
            }
            if(max < 0) max = -max;
            max = max.toString().length();
            int [][] bd = new int[19][arr.length];
            for(int k = 0; k < max; k++) {
                for (int i = 0; i < arr.length; i++) {
                    int value = (int)(arr[i] / (Math.pow(10,k)) % 10);
                    bd[value+9][bk[value+9]++] = arr[i];
                }
                int fl = 0;
                for(int l = 0; l < 19; l++){
                    if(bk[l] != 0){
                        for(int s = 0; s < bk[l]; s++){
                            arr[fl++] = bd[l][s];
                        }
                    }
                }
                bk = new int[19];
                fl = 0;
            }
        }
        
        /**
         * 归并排序 - 采用了分治和递归的思想,
         递归&分治-排序整个数列如同排序两个有序数列,
         依次执行这个过程直至排序末端的两个元素,
         再依次向上层输送排序好的两个子列进行排序直至整个数列有序(类比二叉树的思想,from down to up)。
         *
         * 时间复杂度:O(NlogN)   稳定性:稳定
         * @param arr
         */
         void mergeSortInOrder(int[] arr,int bgn,int mid, int end){
            int l = bgn, m = mid +1, e = end;
            int[] arrs = new int[end - bgn + 1];
            int k = 0;
            while(l <= mid && m <= e){
                if(arr[l] < arr[m]){
                    arrs[k++] = arr[l++];
                }else{
                    arrs[k++] = arr[m++];
                }
            }
            while(l <= mid){
                arrs[k++] = arr[l++];
            }
            while(m <= e){
                arrs[k++] = arr[m++];
            }
            for(int i = 0; i < arrs.length; i++){
                arr[i + bgn] = arrs[i];
            }
        }
         void mergeSort(int[] arr, int bgn, int end)
        {
            if(bgn >= end){
                return;
            }
            int mid = (bgn + end) >> 1;
            mergeSort(arr,bgn,mid);
            mergeSort(arr,mid + 1, end);
            mergeSortInOrder(arr,bgn,mid,end);
        }
        
        /**
         * 堆排序 - 堆排序的思想借助于二叉堆中的最大堆得以实现。
         首先,将待排序数列抽象为二叉树,并构造出最大堆;
         然后,依次将最大元素(即根节点元素)与待排序数列的最后一个元素交换
         (即二叉树最深层最右边的叶子结点元素);
         * 每次遍历,刷新最后一个元素的位置(自减1),直至其与首元素相交,即完成排序。
         *
         * 时间复杂度:O(NlogN)   稳定性:不稳定
         *
         * @param arr
         */
         void heapSort(int[] nums) {
            int size = nums.length;
            for (int i = size/2-1; i >=0; i--) {
                adjust(nums, size, i);
            }
            for (int i = size - 1; i >= 1; i--) {
                int temp = nums[0];
                nums[0] = nums[i];
                nums[i] = temp;
                adjust(nums, i, 0);
            }
        }
        void adjust(int []nums, int len, int index) {
            int l = 2 * index + 1;
            int r = 2 * index + 2;
            int maxIndex = index;
            if (l<len&&nums[l]>nums[maxIndex])maxIndex = l;
            if (r<len&&nums[r]>nums[maxIndex])maxIndex = r;
            if (maxIndex != index) {
                int temp = nums[maxIndex];
                nums[maxIndex] = nums[index];
                nums[index] = temp;
                adjust(nums, len, maxIndex);
            }
        }
    }
    

179. 最大数

  • 题目描述

    image.png

  • 题解

    class Solution {
        public String largestNumber(int[] nums) {
            int n = nums.length;
            // 转换成包装类型,以便传入 Comparator 对象(此处为 lambda 表达式)
            Integer[] numsArr = new Integer[n];
            for (int i = 0; i < n; i++) {
                numsArr[i] = nums[i];
            }
    
            Arrays.sort(numsArr, (x, y) -> {
                long sx = 10, sy = 10;
                while (sx <= x) {
                    sx *= 10;
                }
                while (sy <= y) {
                    sy *= 10;
                }
                return (int) (-sy * x - y + sx * y + x);
            });
    
            if (numsArr[0] == 0) {
                return "0";
            }
            StringBuilder ret = new StringBuilder();
            for (int num : numsArr) {
                ret.append(num);
            }
            return ret.toString();
        }
    }
    

56. 合并区间

  • 题目描述

    image.png

  • 题解

    class Solution {
        public int[][] merge(int[][] intervals) {
            List<int[]> res=new LinkedList<>();
            Arrays.sort(intervals,(o1,o2)->Integer.compare(o1[0],o2[0]));
            int start=intervals[0][0];
            for(int i=1;i<intervals.length;i++){
                if(intervals[i][0]>intervals[i-1][1]){
                    res.add(new int[]{start,intervals[i-1][1]});
                    start=intervals[i][0];
                }else{
                    intervals[i][1]=Math.max(intervals[i][1],intervals[i-1][1]);
                }
            }
            res.add(new int[]{start,intervals[intervals.length-1][1]});
            return res.toArray(new int[res.size()][]);
        }
    }