面试之算法

一、合并两个有序数组

/*给你两个按非递减顺序排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,
其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。*/

package com.itkxw.BrushQuestions;

import java.util.Arrays;

/**
 * @Description:
 * @ClassName: Test01
 * @Author: 康小汪
 * @Date: 2023/11/9 21:35
 * @Version: 1.0
 */
/*给你两个按非递减顺序排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
        请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
        注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,
        其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。*/
public class Test01 {
    public static void main(String[] args) {
        int[] nums1 = {};
        int[] nums2 = {};
        int m = nums1.length - 3;
        int n = nums2.length;
        merge(nums1,m,nums2,n);
    }
    public static void merge(int[] nums1, int m, int[] nums2, int n) {
        for (int i = 0; i < n; i++) {
            nums1[m+i]=nums2[i];
        }
        Arrays.sort(nums1);
        for (int i = 0; i < nums1.length; i++) {
            System.out.print(nums1[i]);
        }
    }
}

二、 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

法一:双指针  

 因为元素的顺序可以改变。也不需要考虑数组中超出新长度后面的元素。

我们可以把输出的数组直接写到输入的数组上。

定义左右指针,右指针指向要处理的元素,如果当前元素是要处理的元素,左指针不动,右指针右移一位;如果当前元素不是要处理的元素,将右指针指向的元素赋值给左指针指向的元素,然后左右指针同时右移一位。

package com.itkxw.BrushQuestions;

/**
 * @Description:
 * @ClassName: Test02
 * @Author: 康小汪
 * @Date: 2023/11/10 11:12
 * @Version: 1.0
 */
/*给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

        不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

        元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。*/
public class Test02 {
    public static void main(String[] args) {
        int[] nums = {0,1,2,2,3,0,4,2};//[1,2,2,3,4,2,4,2]
        int val = 0;
        int i = removeElement(nums, val);
        System.out.println(i);
        System.out.print("[");
        for (int i1 = 0; i1 < nums.length; i1++) {
            if (i1!=nums.length-1){
                System.out.print(nums[i1]+",");
            }else {
                System.out.print(nums[i1]+"]");
            }
        }
    }
    public static int removeElement(int[] nums, int val) {
        int left = 0;
        for (int right = 0; right < nums.length; right++) {
            if (nums[right]!=val){
                nums[left] = nums[right];
                left++;
            }
        }
        return left;
    }
}

法二:双指针优化 

 让两个指针分别指向数组首位元素,从两边遍历数组,如果第一个元素是要处理的元素,直接用尾指针指向的元素替换它,然后尾指针往前移一位;否则左指针往后移一位。

package com.itkxw.BrushQuestions;

/**
 * @Description:
 * @ClassName: Test02
 * @Author: 康小汪
 * @Date: 2023/11/10 11:12
 * @Version: 1.0
 */
/*给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

        不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

        元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。*/
public class Test02 {
    public static void main(String[] args) {
        int[] nums = {0,0,2,2,3,0,4,0};//[1,2,2,3,4,2,4,2]
        int val = 0;
        int i = removeElement(nums, val);
        System.out.println(i);
        System.out.print("[");
        for (int i1 = 0; i1 < nums.length; i1++) {
            if (i1!=nums.length-1){
                System.out.print(nums[i1]+",");
            }else {
                System.out.print(nums[i1]+"]");
            }
        }
    }
    public static int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length;
        while (left

三、验证回文串 

 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

 先利用Character.isLetterOrDigit(c)判断字符是否为字母或数字,如果是,将它添加到StringBuffer中,然后用StringBuffer中的reverse()方法反转字符串

package com.itkxw.BrushQuestions;

/**
 * @Description:
 * @ClassName: Test03
 * @Author: 康小汪
 * @Date: 2023/11/10 18:08
 * @Version: 1.0
 */
/*如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
        字母和数字都属于字母数字字符。
        给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。*/
public class Test03 {
    public static void main(String[] args) {
        String str = "race a car";
        System.out.println(isPalindrome(str));
    }
    public static boolean isPalindrome(String s) {
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(Character.isLetterOrDigit(c)){
                stringBuffer.append(c);
            }
        }
        String str = new StringBuffer(stringBuffer).toString().toLowerCase();
        String string = stringBuffer.reverse().toString().toLowerCase();
        if (str.equals(string)) {
            return true;
        }
        return false;
    }
}

 四、无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

 滑动窗口+HashMap

左右两个指针,左指针指向字符串头部的前一个位置,右指针指向字符串第一个位置;判断hashmap中的key有没有这个元素,如果有,更新左指针,如果没有,更新右指针,更新结果

package com.itkxw.BrushQuestions;

import java.util.HashMap;

/**
 * @Description:
 * @ClassName: Test04
 * @Author: 康小汪
 * @Date: 2023/11/10 19:25
 * @Version: 1.0
 */
//给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。
public class Test04 {
    public static void main(String[] args) {
        String str = "abcabcfbb";
        System.out.println(lengthOfLongestSubstring(str));
    }
    public static int lengthOfLongestSubstring(String s) {
        HashMap hashMap = new HashMap<>();
        int left = -1;
        int count = 0;
        int length = s.length();
        for (int right = 0; right < length; right++){
            if (hashMap.containsKey(s.charAt(right))){
                left = Math.max(left,hashMap.get(s.charAt(right)));//更新左指针
            }
            hashMap.put(s.charAt(right),right);//添加到哈希表
            count = Math.max(count,right-left);//更新结果
        }
        return count;
    }
}

五、长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

 滑动窗口

    public static int minSubArrayLen(int target, int[] nums) {
        int left = 0;//左指针
        int right = 0;//右指针
        int minLength = 0;//最小长度
        int temp = 0;//和
        while (right=target){
                if (right - left + 1 < minLength || minLength == 0) {
                    minLength = right - left + 1;//更新最优结果
                }
                temp = temp - nums[left];//滑动窗口,缩小窗口
                left++;
            }
            right++;
        }
        return minLength;
    }

六、最后一个单词的长度

 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

从String尾部遍历字符串,如果尾部有空格,先过滤掉空格
如果尾部没有空格,在遇到第一个空格时,空格后面的那个字符串就是字符串中最后一个最大字串

public static int lengthOfLastWord(String s) {
        //从String尾部遍历字符串,如果尾部有空格,先过滤掉空格
        //如果尾部没有空格,在遇到第一个空格时,空格后面的那个字符串就是字符串中最后一个最大字串
        int index = s.length() - 1;
        int lengthStr = 0;
        while (s.charAt(index)==' '){
            index--;
        }
        while (index>=0&&s.charAt(index)!=' '){
            lengthStr++;
            index--;
        }
        return lengthStr;
    }

七、删除有序数组中的重复项 

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

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

 双指针

一个快指针,一个慢指针,因为数组是有序的,所以重复元素肯定相邻。让两个指针开始都指向1索引处的元素,比较快指针和它前一个位置的元素是否相等,相等的话就后移快指针,不相等的话,就把快指针位置的元素赋值给慢指针指向的位置,直到快指针到达数组末尾,然后返回慢指针的索引就是删除后数组的长度。

        int len = nums.length;
        int fast = 1;
        int slow = 1;
        if (len==0){
            return 0;
        }
        while (fast

八、回文数

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

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

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

法一:转字符串 

 简单粗暴,看看就行

将整数转成字符串,比较字符串与它反转之后是否相等。

    public static boolean isPalindrome(int x) {
        StringBuilder stringBuilder = new StringBuilder(x+"");
        String string = stringBuilder.reverse().toString();
        return string.equals(x+"");
    }

 法二:数学解法

一个整数是否回文,只需拿到后半段的反转与前半段比较就行,比如1221,后半段 反转是12,等于前半段,他是回文数。

那么怎么取到后半段呢?假设整数为y

先取最低位:x=y%10

将取到的最低位加到取出数的末尾:z=z*10+x

每取一次最低位,整数都要除以10 

public static boolean isPalindrome(int x) {
        //如果是负数或者最后一位数是0,直接返回false
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }
        int endX = 0;
        //当前半段大于后半段时执行循环
        while (x > endX) {
            endX = endX * 10 + x % 10;
            x = x / 10;
        }
        return (x==endX)||(x==endX/10);
    }

九、加一 

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

 只关注末尾有没有9,假如末尾是9,比如:[1,2,3,9],我们只需要将3+1,将9置0即可;

没有9,比如[1,2,3],直接3+1就行;都是9,比如[9,9,9],这是我们就要将数组长度加1[1,0,0,0]。

    public static int[] plusOne(int[] digits) {
        int len = digits.length;
        for (int i = len - 1; i >= 0; i--) {//逆序循环遍历数组
            if (digits[i] != 9) {//这个元素不为9,就直接加1
                digits[i]++;
                //当前元素为9时,将当前元素后面的元素置为0
               for (int j = i+1;j

十、删除有序数组中的重复项II

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

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

 思路可以参考 第七题 ;

定义两个指针,遍历数组,找出应该保留的元素,将不应该保留的元素直接原地替换。

    public static int removeDuplicates(int[] nums) {
        int len = nums.length;
        //重复元素只能出现两次,如果长度小于等于2,
        //符合题目要求,直接返回数组长度
        if (len<=2){
            return len;
        }
        int fast = 2;
        int slow = 2;
        while (fast

十一、多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

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

法一:  哈希表

使用哈希映射来存储每个元素以及出现的次数,对于hashmap中的每个键值对,键表示一个元素,值表示该元素出现的次数。

  public static int majorityElement(int[] nums) {
        HashMap hm = new HashMap<>();
        //遍历数组,将数组中的元素作为hashmap的键
        //重复元素将对应value的值加1
        for (int num : nums) {
            if (!hm.containsKey(num)){
                hm.put(num,1);
            }else {
                hm.put(num, hm.get(num)+1);
            }
        }
        int n = nums.length/2;
        int a = 0;
        for(Map.Entry entry: hm.entrySet()){
            if (entry.getValue()>n){
                  a =  entry.getKey();}

        }
        return a;
    }

 法二:摩尔投票法

摩尔投票法就相当于候选人统计票数,遇到相同的,票数+1,遇到不同的票数-1,当票数为0的时候,会更换候选人,最终剩下的就是胜出者;

 题目说,多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素,那么超过一半的这个元素就相当于那个胜出的候选人。

        // 摩尔投票法
        //假设第一个元素是第一个候选人,他的票数设置为1
        int candidate = nums[0];
        int count = 1;
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            if (candidate == nums[i]){//如果下一票还是他,那么他的票数+1,否则-1
                count++;
            }else {
                count--;
                if (count==0){//当候选人的票数为0时,更换候选人,并且将票数置为1
                    candidate = nums[i];
                    count=1;
                }
            }
        }
        return candidate;

 十二、多数元素II

给定一个大小为 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 

 参考 第十一题 的思路 ,使用摩尔投票法。

十三、轮转数组

法一:创建额外数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

 创建新数组,将元素按反转规则加入新数组,然后将新数组拷贝到原始数组。

    public static void rotate(int[] nums, int k) {
        // 1. 使用新数组解决问题
        int len = nums.length;
        int[] newArr = new int[len];
        //数组翻转,放进新的数组里面
        for (int i = 0; i < len; i++) {
            newArr[(i + k) % len] = nums[i];
        }
        System.arraycopy(newArr, 0, nums, 0, len);
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i]);
        }
    }

 法二:数组翻转

 我们可以将数组全部反转,这样需要翻转的数组就会跑到数组前面,然后按照需要翻转的个数将数组分成两部分,各自翻转就ok了。

k = 3

原数组 : 1,2,3,4,5,6,7

全部翻转 : 7,6,5,4,3,2,1

将数组分为 :7,6,5     和 4,3,2,1两个数组,进行各自翻转

结果 : 5,6,7,1,2,3,4

   public static void rotate(int[] nums, int k) {
        // 2. 翻转数组
        int len = nums.length;
        k %= len;
        reverse(nums, 0, len - 1);
        reverse(nums, 0, k - 1);
        int[] reverse = reverse(nums, k, len - 1);
        for (int i = 0; i < reverse.length; i++) {
                System.out.print(reverse[i]);
            }
    }

    public static int[] reverse(int[] nums, int start, int end) {

        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
        return nums;
    }

 十二、买股票的最佳时机

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

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

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

 题目要求最大利润,就是找出股票最高点卖出,最低点买入。并且今天买只能以后卖。

从第一天开始找,找到最低点,如果不是最低点,继续找,如果是就找以后的最高点,计算出最高利润。

    public static int maxProfit(int[] prices) {
        int len = prices.length;
        int max = 0;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < len; i++) {
            if (prices[i]max) {
                max = prices[i] - min;
            }
        }
        return max;
    }

 

 

 

 

 

 

你可能感兴趣的:(算法,算法,数据结构)