剑指offer刷题记录(三)

1.

思路一:双栈的思路,就是一个valuestack,来存放值得,不管最小值。另一个是minstack,来存放最小值的

20ms

class MinStack {
    Deque<Integer> A, B;
    public MinStack() {
        A = new LinkedList<Integer>();
        B = new LinkedList<Integer>();
    }
    public void push(int x) {
        A.push(x);
        if(B.isEmpty() || B.peek() >= x)
            B.push(x);
    }
    public void pop() {
        if(A.pop().equals(B.peek()))
            B.pop();
    }
    public int top() {
        return A.peek();
    }
    public int min() {
        return B.peek();
    }
}

思路二:非常难想到,但是速度挺快 17ms 使用链表的表结构来表示这个栈,把这个最小值作为表结构里面的一个属性

class MinStack {
 private Node head; 

    public MinStack() {

    }

    public void push(int x) {

        if (head == null)
            head = new Node(x, x, null);
        else
            head = new Node(x, Math.min(head.min, x), head);
    }

    public void pop() {

        head = head.next;
    }

    public int top() {

        return head.val;
    }

    public int min() {

        return head.min;
    }

    private class Node {

        int val;
        int min;
        Node next;

        public Node(int val, int min, Node next) {

            this.val = val;
            this.min = min;
            this.next = next;
        }
    }
}

2.

我的解法:0ms 实际上就是链表删除

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode tmp = head;
        while(head != null){
            if(head.val == val){
            tmp = head.next;
            head.next =null;
            return tmp;
            }else if(head.next.val == val){
                head.next = head.next.next;
                return tmp;
            }else{
                head = head.next;
            }
        }
        return tmp;
    }
}

3.

递归:1ms 思路因为每一个点左右子树最终的层数相减的绝对值都不能大于1,所以依次就行递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    boolean tmp = true; ;
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        height(root);
        return tmp;
    }
    public int height(TreeNode root){
        if(root == null) return 0;
        int left = height(root.left);
        int right = height(root.right);
        if(Math.abs(left-right) > 1){
            tmp = false;
        }
        return Math.max(left,right)+1;
    }
}

4.

这道题乍一看很容易,但其实有难度的,我一开始想到的是HashMap,key存放字符,value存放的是出现次数,然后遍历这个map,第一个出现value值为1,那就是这个字符,但是发现HashMap是无序的,第一出现value值为1不一定是第一个出现一次的字符。这就很麻烦了,后来发现LinkedHashMap完美地解决了这个问题

解法一:LinkedHashMap  37ms 这个思路就是简单但是效率低

class Solution {
    public char firstUniqChar(String s) {
        Map<Character, Integer> map = new LinkedHashMap<>();
        for(int i=0; i<s.length(); i++){
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i),0)+1);
        }
        for(char sss : map.keySet()){
            if(map.get(sss) == 1) return sss;
        }
        return ' ';
    }
}

解法二 难以想到但是效率很高

class Solution {
    public char firstUniqChar(String s) {
        int[] count = new int[26];
        for(char c : s.toCharArray()){
            count[c - 'a']++;
        }
        for(char c : s.toCharArray()){
            if(count[c - 'a'] == 1) return c;
        }
        return ' ';
    }
}

4.

解法一:递归

具体的实现思路,这个大佬已经写得非常详细和准确,直接转载过来。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        boolean tmp = helper(root.left,root.right);
        return tmp;
    }
    public boolean helper(TreeNode L,TreeNode R){
        if(L == null && R == null) return true;
        if(L == null || R == null || L.val != R.val) return false;
        return helper(L.left,R.right) && helper(L.right,R.left);
    }
}

方法二:层次遍历法 非递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {

    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }

        // 实现类不能选用 ArrayDeque,因为该类的 add 方法会对添加的元素做非空检查
        Deque<TreeNode> deque = new LinkedList<>();
        deque.addFirst(root.left);
        deque.addLast(root.right);

        while (!deque.isEmpty()) {
            TreeNode leftNode = deque.removeFirst();
            TreeNode rightNode = deque.removeLast();

            if (leftNode == null && rightNode == null) {
                continue;
            }

            if (leftNode == null || rightNode == null) {
                return false;
            }

            if (leftNode.val != rightNode.val) {
                return false;
            }

            deque.addFirst(leftNode.right);
            deque.addFirst(leftNode.left);
            deque.addLast(rightNode.left);
            deque.addLast(rightNode.right);
        }

        return true;
    }
}

5.

1.我的思路就比较复杂,(N-1)*(N-1)的复杂度,输出数组上每一个数都要遍历一遍原来的数组。

2.转载一个大佬的思路它只遍历两遍

class Solution {
    public int[] constructArr(int[] a) {
        if (Objects.isNull(a) || a.length == 0) {
            return new int[]{};
        }
        int[] res = new int[a.length];
        int left = 1;
        for (int i = 0; i < res.length; i++) {
            res[i] =left;
            left *= a[i];
        }
        int right = 1;
        for (int i = res.length - 1; i >= 0; i--) {
            res[i] *= right;
            right *= a[i];
        }
        return res;
    }
}

6.

解法:动态规划,下面代码是我复现大佬的思想的

class Solution {
    public int maxSubArray(int[] nums) {
        int [] dp = new int[nums.length];
        dp[0]= nums[0];
        int res = nums[0];
        for(int i=1;i<nums.length;i++){
            if(dp[i-1]>0){
                dp[i] = dp[i-1] + nums[i];
            }else{
                dp[i] = nums[i];
            }
        }
        for(int i=0;i<nums.length;i++){
            if(dp[i] > res){
                res = dp[i];
            }
        }
        return res;
    }
}

但看了大佬的代码,发现更是简单

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        for(int i = 1; i < nums.length; i++) {
            nums[i] += Math.max(nums[i - 1], 0);
            res = Math.max(res, nums[i]);
        }
        return res;
    }
}

7.

解法1:先排序,然后把最前面的K个数输出。这里排序就有很多种了

1.使用Arrays.sort方法,这办法就是简单,速度不慢,因为该办法使用的是快速排序。

2.使用堆排序

3.快速排序

除了1方法外,其余的都需要自己去手写排序算法,我估计在面试时可能不会让你使用方法1,而是让你手写,所以几种排序方法,先慢慢练着去写。

下面就是手写堆排序,16ms,速度还可以。

class Solution {
    public int[] getLeastNumbers(int[] a, int k) {
        
        int [] arr = Arrays.copyOf(a,a.length);
        int len = a.length;
        buildMaxHeap(arr,len);
        for(int i = len - 1;i>0;i--){
            swap(arr,0,i);
            len--;
            heapify(arr,0,len);
        }
        int [] res = new int[k];
        for(int j=0;j<k;j++){
            res[j] = arr[j];
        }
        return res;
        
    }
    void buildMaxHeap(int[] arr,int len){//创建一个最大堆
        for(int i = (int)Math.floor(len/2);i>=0;i--){
            heapify(arr,i,len);
        }
    }
    void heapify(int[] arr,int i,int len){//堆化,就是乱序堆变成最大堆
        int left = 2*i+1;
        int right = 2*i+2;
        int largest = i;
        if(left <len && arr[left] > arr[largest]){
            largest = left;
        }
        if(right <len && arr[right] > arr[largest]){
            largest = right;
        }
        if(largest != i){
            swap(arr,largest,i);
            heapify(arr,largest,len);
        }

    }
    void swap(int[] arr,int i,int j ){//用于交换的方法
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

}

使用sort()方法

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        Arrays.sort(arr);
        int[] res = new int[k];
        for (int i = 0; i < k; i++) 
            res[i] = arr[i];
        return res;
    }
}

手写快排:速度和sort()方法几乎一样,但必须要会,万一面试需要你手写呢

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        int left = 0;
        int right = arr.length -1;
        quickSort(arr,left,right);
        int [] res = new int[k];
        for(int j=0;j<k;j++){
            res[j] = arr[j];
        }
        return res;

    }
    public void quickSort(int [] arr,int left,int right) {
	      int pivot = 0;
	      if(left < right) {
	         pivot = partition(arr,left,right);
	         quickSort(arr,left,pivot-1);
	         quickSort(arr,pivot+1,right);
	      }
    }
    private static int partition(int[] arr,int left,int right) {
 
	    int key = arr[left];
	    while(left < right) {
	        while(left < right && arr[right] >= key) {
	            right--;
	        }
	        arr[left] = arr[right];
	        while(left < right && arr[left] <= key) {
	            left++;
	        }
	        arr[right] = arr[left];
	    }
	    arr[left] = key;
	    return left;
	}
}

解法二:一个大佬的解法,速度非常快2ms

先创建一个大小为10001的数组line(随便起的名字),默认值是0不用管。
遍历arr,每次将arr[i]的值作为line的下标,然后line[arr[i]]++。这样遍历完后,line这个数组存放的就是arr中每个值存放的次数。如arr=[3,2,1]。那么最后line=[0,1,1,1,0.,0..],又如arr=[0,1,2,1],则line=[1,2,1,0....]

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k ==0) return new int[0];
        int[] line = new int[10001];
        int[] res = new int[k];
        for(int i=0;i<arr.length;i++){
            line[arr[i]]++;
        }
        int count =0;
        for(int i=0;i<line.length;i++){
            if(line[i]>0){
                int n = line[i];
                while(n>0){
                    res[count++] = i;
                    n--;
                    k--;
                    if(k == 0) return res;
                }
            }           
        }
        return res;
	}
}

8.

我的解法:超出了时间限制

class Solution {
    public int lastRemaining(int n, int m) {
        int count = 0;
        if(n==1) return 0; 
        Queue<Integer> queue = new LinkedList<Integer>();
        for(int i=0;i<n;i++){//添加元素
            queue.offer(i);
        }
        while(queue.size() != 1){
            int element = queue.poll();
            count++;
            if(count == m){
                count = 0;
            }else{
                queue.offer(element);
            }
        }
        return queue.poll();
    }
}

解法一:在我基础上修改,不再计数,也不再依次弹出后,再加到队列最后面。而是使用ArrayList,因为每次删除掉的元素的下表都是有规律的,第一个被删除的元素下标为(m-1)%list.size并且记为c ,之后被删除的元素下标为(c+m-1)%list.size。

class Solution {
    public int lastRemaining(int n, int m) {
        if(n==0||m==0)
            return -1;
        List<Integer> list=new ArrayList<>();
        for(int i=0;i<n;i++)
            list.add(i);
        int c=(m-1)%n;
        while(list.size()!=1) {
            list.remove(c);
            c=(c+m-1)%list.size();  
        }
        return list.get(0);
    }
}

解法二和三:参考一个大佬的思路,运用数学思想,一步一步递归,思路如下:

这样这个思路可以用递归来逐步退出,也可以用迭代(动态规划)来解。

//迭代
public int lastRemaining(int n, int m) {
    int flag = 0;   
    for (int i = 2; i <= n; i++) {
        flag = (flag + m) % i;
        //动态规划的思想,将f(n,m)替换成flag存储
    }
    return flag;
}

//递归
public int lastRemaining(int n, int m){
    if(n < 1 || m < 1)       
        return -1;
    if(n == 1)
        return 0;
    return (lastRemaining(n-1, m) + m) % n;
}

9.

解法:使用二进制的异或和位运算

class Solution {
    public int add(int a, int b) {
        while (a != 0) {//所以循环结束条件就是直到不存在进位了,那temp就是最终结果
            int temp = a ^ b;//代表没有进位的加法
            a = (a & b) << 1; //代表进位
            b = temp;  //不停循环代表:把没有进位的加上进位不就是原数了嘛
        }
        return b;
    }
}

 

10.

这道题比较有难度的,有的用是动态规划的思路,我是完全没有做出来。

class Solution {
    public double[] twoSum(int n) {
        int [][]dp = new int[n+1][6*n+1];
        //边界条件
        for(int s=1;s<=6;s++)dp[1][s]=1;
        for(int i=2;i<=n;i++){
            for(int s=i;s<=6*i;s++){
                //求dp[i][s]
                for(int d=1;d<=6;d++){
                    if(s-d<i-1)break;//为0了
                    dp[i][s]+=dp[i-1][s-d];
                }
            }
        }
        double total = Math.pow((double)6,(double)n);
        double[] ans = new double[5*n+1];
        for(int i=n;i<=6*n;i++){
            ans[i-n]=((double)dp[n][i])/total;
        }
        return ans;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值