剑指offer

58.字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:

输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
 

限制:

1 <= k < s.length <= 10000

class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n)+s.substring(0,n);
    }
}
class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] c=new char[s.length()];
        int index=0;
        for(int i=n;i<s.length();i++){
            c[index++]=s.charAt(i);
        }
        for(int i=0;i<n;i++){
            c[index++]=s.charAt(i);
        }
        return new String(c);
    }
}
class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        for(int i = n; i < n + s.length(); i++)
            res.append(s.charAt(i % s.length()));
        return res.toString();
    }
}

64.求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:

输入: n = 3
输出: 6
示例 2:

输入: n = 9
输出: 45
 

限制:

1 <= n <= 10000

class Solution {
    public int sumNums(int n) {
        //递归
        boolean flag=n>0&&(n+=sumNums(n-1))>0;
        return n;
    }
}

 65.写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

示例:

输入: a = 1, b = 1
输出: 2
 

提示:

a, b 均可能是负数或 0
结果不会溢出 32 位整数

class Solution {
    public int add(int a, int b) {
        if(b==0){
            return a;
        }else{
            return add(a^b,(a&b)<<1);
        }
    }
}

50.在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例 1:

输入:s = "abaccdeff"
输出:'b'
示例 2:

输入:s = "" 
输出:' '
 

限制:

0 <= s 的长度 <= 50000

 浅做一个哈希表

class Solution {
    public char firstUniqChar(String s) {
        //注意不要用char,用Character
        Map<Character, Integer> frequency = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            //if不好看
            frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);
        }
        for (int i = 0; i < s.length(); ++i) {
            if (frequency.get(s.charAt(i)) == 1) {
                return s.charAt(i);
            }
        }
        return ' ';
    }
}

队列学习一下

class Solution {
    public char firstUniqChar(String s) {
        Map<Character, Integer> position = new HashMap<Character, Integer>();
        Queue<Pair> queue = new LinkedList<Pair>();
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            char ch = s.charAt(i);
            if (!position.containsKey(ch)) {
                position.put(ch, i);
                queue.offer(new Pair(ch, i));
            } else {
                position.put(ch, -1);
                while (!queue.isEmpty() && position.get(queue.peek().ch) == -1) {
                    queue.poll();
                }
            }
        }
        return queue.isEmpty() ? ' ' : queue.poll().ch;
    }

    class Pair {
        char ch;
        int pos;

        Pair(char ch, int pos) {
            this.ch = ch;
            this.pos = pos;
        }
    }
}

52.输入两个链表,找出它们的第一个公共节点。

如下面的两个链表:

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
 

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
 

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
 

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
本题与主站 160 题相同:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         //双指针
        if(headA==null||headB==null){
            return null;
        }
        ListNode t1=headA;
        ListNode t2=headB;
        
        while(t1!=t2){
            //相当于尾部加了一个null,如果根本没有交点会在尾部相遇
            t1=t1!=null?t1.next:headB;
            t2=t2!=null?t2.next:headA;
        }
        return t1;
    }
}

 哈希表可以但没必要。

53.统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
 

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
 

注意:本题与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

class Solution {
    public int search(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return rightIdx - leftIdx + 1;
        } 
        return 0;
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length - 1, ans = nums.length;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid - 1;
                ans = mid;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}

53.一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2
示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8
 

限制:

1 <= 数组长度 <= 10000

class Solution {
    public int missingNumber(int[] nums) {
        //简单遍历
        int n=nums.length;
        for(int i=0;i<n;i++){
            if(nums[i]!=i){
                return i;
            }
        }
        return n;
    }
}

class Solution {
    public int missingNumber(int[] nums) {
        //二分法
        int n=nums.length;
        int left=0;
        int right=n-1;
        int temp=0;
        while(left<=right){
            temp=(left+right)/2;
            if(nums[temp]==temp){
                left=temp+1;
            }else{
                right=temp-1;
            }
        }
        return left;
    }
}

43.输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6
 

限制:

1 <= n < 2^31

起手一个经典超时

class Solution {
    public int countDigitOne(int n) {
        int result=0;
        for(int i=1;i<=n;i++){
            result+=numberOne(i);
        }
        return result;
    }
    //写一个方法判断输入数的1的个数
    public int numberOne(int n){
        int res=0;
        while(n>0){
            if(n%10==1){
                res++;
            }
            n/=10;
        }
        return res;
    }
}

想想动态规划

class Solution {
    public int countDigitOne(int n) {
        int[] result=new int[n+1];
        for(int i=1;i<=n;i++){
            result[i]=result[i-1]+numberOne(i);
        }
        return result[n];
    }
    //写一个方法判断输入数的1的个数
    public int numberOne(int n){
        int res=0;
        while(n>0){
            if(n%10==1){
                res++;
            }
            n/=10;
        }
        return res;
    }
}

好,一个经典的超内存。不需要存储之前的所有值,事实上只需要一个前面的值和现在的值。

class Solution {
    public int countDigitOne(int n) {
        int a=0;
        int b=0;
        for(int i=1;i<=n;i++){
            b=a+numberOne(i);
            a=b;
        }
        return b;
    }
    //写一个方法判断输入数的1的个数
    public int numberOne(int n){
        int res=0;
        while(n>0){
            if(n%10==1){
                res++;
            }
            n/=10;
        }
        return res;
    }
}

好的,又是一个优秀的超时!优化函数?

class Solution {
       private int dfs(int n) {
           //1.确定边界条件
           if(n==0){
               return 0;
           }
           //2.根据首位是否为1递归判断1的个数
           String a=String.valueOf(n);
           int c=a.charAt(0)-'0';
           int temp=(int)Math.pow(10,a.length()-1);
           int k=n-c*temp;//除去首位数剩下的数
           if(c==1){
               //如果首位是1
               //如果是1234,那就是234里1的个数+0-999里1的个数、固定首位为1余下的235个。
               return dfs(k)+dfs(temp-1)+k+1;
           }else{
               //首位不是1
               //如果是2234,那就是234里1的个数+两个0-999里1的个数,固定首位为1的一千个数。
               return dfs(k)+c*dfs(temp-1)+temp;
           }
    }

    // 递归求解
    public int countDigitOne(int n) {
        return dfs(n);
    }
}

过辣!

14.给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:

2 <= n <= 58

class Solution {
    int a=0;
    int b=0;
    public int cuttingRope(int n) {
        //显然均分会是最好的结果,然后判断一手极大值求导,可以得出e的时候最大,于是优先取3.
        if(n<=3){
            return n-1;
        }
        a=n/3;
        b=n%3;
        if(b==1){
            return (int)Math.pow(3,a-1)*4;
        }
        if(b==0){
            return (int)Math.pow(3,a);
        }
        return (int)Math.pow(3,a)*2;
    }
}

 代码应该有个动态规划解法。

class Solution {
    public int cuttingRope(int n) {   
        int[] dp = new int[n+1];
        dp[2] = 1;
        // i表示绳子长度,j表示这次剪多长
        for (int i=3;i<=n;++i){
            for (int j=2;j<i;++j){
                //有三种情况:1.不剪;2.剪去j并且剩余的部分也剪;3.剪去j剩余的部分不剪。
                dp[i]=(int)Math.max(Math.max(j*(i-j),j*dp[i-j]),dp[i]);
            }
        }
        return dp[n];
    }
}

57.输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
 

限制:

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^6

又是经典超时!

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> hashMap=new HashMap<>();
        int n=nums.length;
        for(int i=0;i<n;i++){
            if(!hashMap.containsValue(target-nums[i])){
                hashMap.put(i,nums[i]);
            }else{
                return new int[]{nums[i],target-nums[i]};
            }
        }
        return new int[0];
    }
}

双指针

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //双指针
        int n=nums.length;
        int j=n-1;
        int i=0;
        while(i<j){
            if(nums[i]+nums[j]>target){
                j--;
            }else if(nums[i]+nums[j]<target){
                i++;
            }else{
                return new int[]{nums[i],nums[j]};
            }
        }
        return new int[0];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值