牛客网剑指offer-其他算法(java实现)

一、JZ66 构建乘积数组(简单)

1、双向遍历

如上图所示,矩阵中由对角线1将其分成了上三角和下三角。我们先看下三角,如果我们累乘的时候,B[1]是在B[0]的基础上乘了新增的一个A[0],B[2]是在B[1]的基础上乘了新增的一个A[1],那我们可以遍历数组的过程中不断将数组B的前一个数与数组A的前一个数相乘就得到了下三角中数组B的当前数。同理啊,我们在上三角中,用一个变量存储从右到左的累乘,每次只会多乘上一个数字。这样,两次遍历就可以解决。

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        //初始化数组B
        int[] B = new int[A.length];
        B[0] = 1;
        //先乘左边,从左到右
        for(int i = 1; i < A.length; i++)
            //每多一位由数组B左边的元素多乘一个前面A的元素
            B[i] = B[i - 1] * A[i - 1];
        int temp = 1;
        //再乘右边,从右到左
        for(int i = A.length - 1; i >= 0; i--){
            //temp为右边的累乘
            B[i] *= temp;
            temp *= A[i];
        }
        return B;
    }
}

二、JZ50 第一个只出现一次的字符(简单)

1、使用辅助ArrayList

import java.util.ArrayList;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        ArrayList<Character> list = new ArrayList<>();
        ArrayList<Character> reapyList = new ArrayList<>();
        for(int i = 0;i< str.length();i++) {
            if(!reapyList.contains(str.charAt(i))) {
                if(list.contains(str.charAt(i))) {
                    reapyList.add(str.charAt(i));
                    list.remove(list.indexOf(str.charAt(i))); 
                } else {
                    list.add(str.charAt(i));
                }
            }  
        }
        if(list.size() == 0) return -1;
         for(int i = 0;i< str.length();i++) {
            if(list.get(0) == str.charAt(i)) {
                return i;
            }
         }
         return -1;
    }
}

2、使用HashMap

import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        HashMap<Character, Integer> mp = new HashMap<>();
        //统计每个字符出现的次数
        for(int i = 0; i < str.length(); i++) 
            mp.put(str.charAt(i), mp.getOrDefault(str.charAt(i), 0) + 1);
        //找到第一个只出现一次的字母
        for(int i = 0; i < str.length(); i++) 
            if(mp.get(str.charAt(i)) == 1)
                return i;
        //没有找到
        return -1; 
    }
}

三、JZ5 替换空格(简单)

1、正常遍历替换

import java.util.*;
public class Solution {
    /** 
     * @param s string字符串 
     * @return string字符串
     */
    public String replaceSpace (String s) {
        // write code here
        StringBuffer str = new StringBuffer();
        for(int i = 0;i< s.length(); i++) {
            if(s.charAt(i) == ' ') {
                str.append("%20");
            } else {
                str.append(s.charAt(i));
            }
        }
        return str.toString();
    }
}

四、JZ21 调整数组顺序使奇数位于偶数前面(一)(中等)

1、在原数组上修改

import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型一维数组 
     * @return int整型一维数组
     */
    public int[] reOrderArray (int[] array) {
        // write code here
        int i = 0;
        for (int j=0; j < array.length; ++j) {
            // 遇到奇数时
            if (array[j] % 2 == 1) {
                // 先将 array[j] 赋值
                int tmp = array[j];
                // 将 【i, j-1】数组后移动
                for (int k=j-1; k>=i; --k) {
                    array[k+1] = array[k];
                }
                // 将array[j]插入到 i++ 的位置
                array[i++] = tmp;
            }
        }
        return array;
    }
}

2、使用奇数偶数辅助数组

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型一维数组 
     * @return int整型一维数组
     */
    public int[] reOrderArray (int[] array) {
        // write code here
        ArrayList<Integer> ji = new ArrayList<>();
        ArrayList<Integer> ou = new ArrayList<>();
        int[] result = new int[array.length];
        int index = 0;
        for(int i= 0;i< array.length;i++) {
            if(array[i] % 2 == 0) {
                ou.add(array[i]);
            } else {
                ji.add(array[i]);
            }
        }
        ji.addAll(ou);
        for(int item: ji) {
            result[index] = item;
            index++;
        }
        return result;
}

五、JZ39 数组中出现次数超过一半的数字(简单)

1、哈希法

import java.util.HashMap;
public class Solution {
    int result = 0;
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer, Integer> hm = new HashMap<>();
        for(int item: array) {
            hm.put(item, hm.getOrDefault(item, 0) + 1);
        }
        hm.forEach((k,v) -> {
            if(v > (array.length / 2)) {
                result = k;
            }
        });
        return result;
    }
}

六、JZ43 整数中1出现的次数(从1到n整数中1出现的次数(中等)

1、暴力法

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        for(int i=1;i<=n;i++){
            String q=String.valueOf(i);
            char [] chars = q.toCharArray();
            for(int j=0;j<chars.length;j++){
                if(chars[j]=='1'){
                    count++;
                }
            }
        }
        return count;
    }
}

2、数学推导

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0, bitNum = 1, high = n / 10, cur = n % 10, low = 0;
        // cur遍历n每一数位
        while(cur != 0 || high != 0) {
            if(cur < 1) {
                // case 1: cur == 0
                // cur=0时,高位需要减去一位用于低位进行计算
                // 相当于 count = (high - 1) * bitNum + (99 + 1)
                count += high * bitNum;
            } else if(cur == 1) {
                // case 2: cur == 1
                // 相当于高位+低位计算结果,即(high * bitNum) + (low + 1)     
                count += high * bitNum + low + 1;
            } else {
                // case3: cur > 1
                // 相对于cur=0的情况,就不需要高位减去一位来计算低位的结果数了
                // 相当于(high * bitNum) + (低位数结果数)
                count += (high + 1) * bitNum;
            }
            // low、cur、high都像左偏移一个位
            // bitNum表示cur的数位
            low += cur * bitNum;
            cur = high % 10;
            high = high / 10;
            bitNum = bitNum * 10;
        }
        return count;
    }
}

七、JZ45 把数组排成最小的数(中等)

1、重载排序

import java.util.*;
public class Solution {
    public String PrintMinNumber(int [] numbers) {
        //空数组的情况
        if(numbers == null || numbers.length == 0) return "";
        String[] str = new String[numbers.length];
        //将数字转成字符
        for(int i = 0;i< numbers.length;i++) {
            str[i] = numbers[i] + "";
        }
        //按照重载排序
        Arrays.sort(str, new Comparator<String>() {
            public int compare(String s1, String s2) {
                return (s1 + s2).compareTo(s2 + s1);
            }
        });
        StringBuffer result = new StringBuffer();
        for(String item: str) {
            result.append(item);
        }
        return result.toString();
    }
}

2、冒泡排序

import java.util.*;
public class Solution {
    public String PrintMinNumber(int [] numbers) {
        //空数组的情况
        if(numbers == null || numbers.length == 0)
            return "";
        String[] nums = new String[numbers.length];
        //将数字转成字符
        for(int i = 0; i < numbers.length; i++)
            nums[i] = numbers[i] + "";
        //冒泡排序
        for(int i = 0; i < nums.length - 1; i++){
            for(int j = 0; j < nums.length - i - 1; j++){
                String s1 = nums[j] + nums[j + 1];
                String s2 = nums[j + 1] + nums[j];
                //比较拼接的大小交换位置
                if(s1.compareTo(s2) > 0){
                    String temp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = temp;
                }
            }
        }
        StringBuilder res = new StringBuilder();
        //字符串叠加
        for(int i = 0; i < nums.length; i++)
            res.append(nums[i]);
        return res.toString();
    }
}

八、JZ49 丑数(中等)

1、依次比较计算

public int GetUglyNumber_Solution(int index) {
        //1 2 3 4 5 6 8
        if(index <= 6)
            return index;   // 加快程序输出

        // 三个变量 后面有大作用!
        int i2 = 0,i3 = 0,i5 = 0;
        int[] res = new int[index];
        res[0] = 1;  // 第一个丑数为 1

        for(int i = 1; i < index; i++){
            // 得到下一个丑数,三者中最小的
            res[i] = Math.min(res[i2]*2,Math.min(res[i3]*3,res[i5]*5));
             /*第一次是 2、3、5比较,得到最小的是2*/
            /*第二次是 4、3、5比较,为什么是4了呢?因为上次2已经乘了一次了,所以接下去可以放的丑数在4、3、5之间*/
            // 所以开头的三个指针就是来标记2 3 5 乘的次数的 
            if(res[i] == res[i2]*2)
                i2++;
            if(res[i] == res[i3]*3)
                i3++;
            if(res[i] == res[i5]*5)
                i5++;
        }
        return res[index-1];
    }

九、JZ74 和为S的连续正数序列(中等)

1、暴力枚举

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
        int sum1 = 0;
        //因为序列至少两个数,因此枚举最多到该数字的一半向下取整
        int up = (sum - 1) / 2; 
        //枚举左区间
        for(int i = 1; i <= up; i++){ 
            //从左区间往后依次连续累加
            for(int j = i; ;j++){ 
                sum1 += j;
                //大于目标和则跳出该左区间
                if(sum1 > sum){ 
                    sum1 = 0;
                    break;
                //等于则找到
                }else if(sum1 == sum){ 
                    sum1 = 0;
                    ArrayList<Integer> temp = new ArrayList<Integer>();
                    //记录线序的数字
                    for(int k = i; k <= j; k++) 
                        temp.add(k);
                    res.add(temp);
                    break;
                }
            }
        }
        return res;
    }
}

2、滑动窗口

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
        //从1到2的区间开始
        for(int l = 1, r = 2; l < r;){ 
            //计算区间内的连续和
            int sum1 = (l + r) * (r - l + 1) / 2; 
            //如果区间内和等于目标数
            if(sum1 == sum){ 
                ArrayList<Integer> temp = new ArrayList<Integer>();
                //记录区间序列
                for(int i = l; i <= r; i++) 
                    temp.add(i);
                res.add(temp);
                //左区间向右
                l++; 
            //如果区间内的序列和小于目标数,右区间扩展
            }else if(sum1 < sum) 
                r++;
            //如果区间内的序列和大于目标数,左区间收缩
            else 
                l++;
        }
        return res;
    }
}

十、JZ57 和为S的两个数字(中等)

1、哈希法

import java.util.*;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        //创建哈希表,两元组分别表示值、下标
        HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
        //在哈希表中查找target-numbers[i]
        for(int i = 0; i < array.length; i++){
            int temp = sum - array[i];
            //若是没找到,将此信息计入哈希表
            if(!mp.containsKey(temp)){ 
                mp.put(array[i], i);
            }
            else{
                //取出数字添加
                res.add(temp);   
                res.add(array[i]);
                break;
            }
        }
        return res;
    }
}

2、双指针

import java.util.*;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        //左右双指针
        int left = 0, right = array.length - 1;
        //对撞双指针
        while(left < right){
            //相加等于sum,找到目标
            if(array[left] + array[right] == sum){
                res.add(array[left]);
                res.add(array[right]);
                break;
            //和太大,缩小右边
            }else if(array[left] + array[right] > sum)
                right--;
            //和太小,扩大左边
            else
                left++;
        }
        return res;
    }
}

十一、JZ58 左旋转字符串(中等)

1、求余得到切割点进行字符串的切割拼接

public class Solution {
    public String LeftRotateString(String str,int n) {
        if(str.length() <= 1 || n == 0) return str;
        n = n % str.length();
        StringBuffer res = new StringBuffer();
        res.append(str.substring(n, str.length()));
        res.append(str.substring(0, n));
        return res.toString();
    }
}

2、三次反转

public class Solution {
    public String LeftRotateString(String str,int n) {
        //取余,因为每次长度为n的旋转数组相当于没有变化
        if(str.isEmpty() || str.length() == 0)
            return "";
        int m = str.length();
        n = n % m; 
        //第一次逆转全部元素
        char[] s = str.toCharArray();
        reverse(s, 0, m - 1); 
        //第二次只逆转开头m个
        reverse(s, 0, m - n - 1);
        //第三次只逆转结尾m个
        reverse(s, m - n, m - 1); 
        return new String(s);
    }
    //反转函数
    private void reverse(char[] s, int start, int end){ 
        while(start < end){
            swap(s, start++, end--);
        }
    }
    //交换函数
    private void swap(char[] s, int a, int b){ 
        char temp = s[a];
        s[a] = s[b];
        s[b] = temp;
    }
}

十二、JZ62 孩子们的游戏(圆圈中最后剩下的数)(中等)

1、我们用约瑟夫思想来解决该题。具体的方法如下:首先定义n个人,报m次的解是f(n,m),n,m以及编号为0的小朋友确定了,最后的幸运儿也就确定了,第一次报数后,m-1的小朋友出列,接着从m开始报数,但是最后的解是一样的,令g(n-1,m)表示第二次抽人,解是等于f(n,m)的,这两种情况的排列方式不同,一个从0开始报,一个从m开始报,所以我们把第二种情况也排成从0开始报,就是每个人的下标都减少m即可,那么最后的幸运儿加上m,就得出在原来序列的下标了,解就是f(n-1,m)+m,构成了一个递归问题。但由于是一个循环的环,超出环从头排起,就对每一次环的长度进行取余。关系式为f(n,m) = (f(n-1,m) + m)%n。

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n == 0 || m == 0) return -1;
        if(n == 1) return 0;
        int x = LastRemaining_Solution(n -1 , m);
        return (m + x) % n;
    }
}

十三、JZ75 字符流中第一个不重复的字符(中等)

1、使用辅助ArrayList

import java.util.ArrayList;
public class Solution {
    //Insert one char from stringstream
    ArrayList<Character> res = new ArrayList<>(); 
      ArrayList<Character> repay = new ArrayList<>(); 
    public void Insert(char ch)
    {   
        if(res.contains(ch)) {
            res.remove(res.indexOf(ch));
            repay.add(ch);
        } else {
            if (!repay.contains(ch)) {
                 res.add(ch);
            }
          
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        if(res.size() > 0) {
            return res.get(0);
        } else {
            return '#';
        }
    
    }
}

2、哈希法

import java.util.*;
public class Solution {
    private StringBuilder s = new StringBuilder();
    private HashMap<Character, Integer> mp = new HashMap<>();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        //插入字符
        s.append(ch);  
        //哈希表记录字符出现次数
        mp.put(ch, mp.getOrDefault(ch, 0) + 1);
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        //遍历字符串
        for(int i = 0; i < s.length(); i++) 
            //找到第一个出现次数为1的
            if(mp.get(s.charAt(i)) == 1)
                return s.charAt(i);
        //没有找到
        return '#'; 
    }
}

十四、JZ14 剪绳子(中等)

1、贪心思想

public class Solution {
    public int cutRope(int target) {
        //不超过3直接计算
        if(target <= 3) 
            return target - 1;
        int res = 1;
        while(target > 4){
            //连续乘3
            res *= 3; 
            target -= 3; 
        }
        return res * target;   
    }
}

2、动态规划

import java.util.*;
public class Solution {
    public int cutRope(int target) {
        //不超过3直接计算
        if(target <= 3) 
            return target- 1;
        //dp[i]表示长度为i的绳子可以被剪出来的最大乘积
        int[] dp = new int[target + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        dp[4] = 4;
        //遍历后续每一个长度
        for(int i = 5; i <= target; i++)
            //可以被分成两份
            for(int j = 1; j < i; j++)
                //取最大值
                dp[i] = Math.max(dp[i], j * dp[i - j]);
        return dp[target];
    }
}

十五、JZ81 调整数组顺序使奇数位于偶数前面(二)(简单)

1、双指针交换法

import java.util.*;
public class Solution {
    /**
     * @param array int整型一维数组
     * @return int整型一维数组
     */
    public int[] reOrderArrayTwo (int[] array) {
        // write code here
        int temp = -1;
        for (int i = 0; i < array.length; i++) {
            if (array[i] % 2 == 0) {
                for (int j = array.length - 1; j > i; j--) {
                    if (j > i) {
                        if (array[j] % 2 == 1) {
                            temp = array[i];
                            array[i] = array[j];
                            array[j] = temp;
                            break;
                        }
                    } else {
                        return array;
                    }
                }
            }
        }
        return array;
    }
}

2、双指针法

import java.util.*;
public class Solution {
    public int[] reOrderArrayTwo (int[] array) {
        int n = array.length;
        int[] res = new int[n];
        //统计奇数个数
        int odd = 0;
        //遍历统计
        for(int i = 0; i < n; i++){ 
            if(array[i] % 2 == 1)
                odd++;
        }
        //x与y分别表示答案中奇偶数的坐标
        int x = 0, y = odd; 
        for(int i = 0; i < n; i++){
            //奇数在前
            if(array[i] % 2 == 1){ 
                res[x] = array[i];
                x++;
            //偶数在后
            }else{ 
                res[y] = array[i];
                y++;
            }
        }
        return res;
    }
}

十六、JZ83 剪绳子(进阶版)(较难)

1、使用快速幂,不然时间复杂度会超

import java.util.*;
public class Solution {
    final int MOD = 998244353;
    private long pow(long n) {
        long ans = 1;
        long a = 3;
        while(n > 0) {
            if(n%2 == 1) ans = (ans * a )% MOD;
            a = (a*a)%MOD;
            n /= 2;
        }
        return ans% MOD;
    }
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param number long长整型 
     * @return long长整型
     */
    public long cutRope (long number) {
        if(number < 4) return number -1;
        if(number % 3 == 0) return pow(number/3) % MOD;
        if(number % 3 == 2) return pow(number/3) * 2 % MOD;
        return pow(number/3 -1) *4% MOD;
    }
}

十七、JZ17 打印从1到最大的n位数(简单)

1、数组输出

import java.util.*;
public class Solution {
    /**
     * @param n int整型 最大位数
     * @return int整型一维数组
     */
    public int[] printNumbers (int n) {
        // write code here
        int num = 1;
        for(int i = 0;i < n; i++) {
            num *= 10;
        }
        int[] res = new int[num -1];
        for(int i =1; i < num; i++) {
            res[i-1] = i;
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值