剑指offer(二)

序列二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {     //基于 bfs  借助队列,还可以 基于先序,借助递归

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root==null){
            return "[]";
        }
        StringBuilder res = new StringBuilder();
        res.append("[");    
        Queue<TreeNode> queue  = new LinkedList<>();
        queue.add(root);
        res.append(root.val+",");
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node.left!=null){
                res.append(node.left.val+",");
                queue.offer(node.left);
            }else{
                res.append("null"+",");
            }
            if(node.right!=null){
                res.append(node.right.val+",");
                queue.offer(node.right);
            }else{
                res.append("null"+",");
            }
        }
        res.deleteCharAt(res.length()-1);
        res.append("]");
        return res.toString();
     }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data=="[]"){
            return null;
        }
    String[] val  = data.substring(1,data.length()-1).split(",");
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode head = new TreeNode(Integer.valueOf(val[0]));
        queue.offer(head);
        int i=1;
        while(!queue.isEmpty()){
            TreeNode root  = queue.poll();
            if(!val[i].equals("null")){
                root.left = new TreeNode(Integer.parseInt(val[i]));
                queue.offer(root.left);
            }
            i++;
            if(!val[i].equals("null")){
                root.right = new TreeNode(Integer.parseInt(val[i]));
                queue.offer(root.right);
            }
            i++;
        }
        return head;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

字符串的排列

class Solution {         //如果没有重复单词  ,普通的回溯   过程记住
    ArrayList<String> ans = new ArrayList<>();
    public String[] permutation(String s) {
        char[] strs = s.toCharArray();
        StringBuilder res = new StringBuilder();
        boolean[] used = new boolean[strs.length];
        for(int i = 0;i<strs.length;i++){
            used[i] = true;
            process(res.append(strs[i]),used,1,strs);
            res.deleteCharAt(res.length()-1);
            used[i] = false;
        }
        return ans.toArray(new String[strs.length-1]);
    }
    public void process(StringBuilder res,boolean[] used, int len, char[] strs){
        if(len==strs.length){
            ans.add(new StringBuilder(res).toString());
            return ;
        }
        for(int  i =0;i<strs.length;i++){
            if(used[i]==false){
                used[i]=true;
                process(res.append(strs[i]),used,len+1,strs);
                res.deleteCharAt(res.length()-1);
                used[i]=false;
            }
        }

    }
    
}
//有重复单词,  要去重!
class Solution {
    ArrayList<String> ans = new ArrayList<>();
    public String[] permutation(String s) {
        char[] strs = s.toCharArray();
        StringBuilder res = new StringBuilder();
        boolean[] used = new boolean[strs.length];
         Arrays.sort(strs);
         process(res,used,0,strs);
        return ans.toArray(new String[strs.length-1]);
    }
    public void process(StringBuilder res,boolean[] used, int len, char[] strs){
        if(len==strs.length){
            ans.add(new StringBuilder(res).toString());
            return ;
        }
        for(int  i =0;i<strs.length;i++){
            if(used[i]==false){
                if(i>0&&strs[i-1]==strs[i]&&!used[i-1]){
                    continue;
                }
                used[i]=true;
                process(res.append(strs[i]),used,len+1,strs);
                res.deleteCharAt(res.length()-1);
                used[i]=false;
            }
        }

    }
}

在这里插入图片描述
1、在图中 ② 处,搜索的数也和上一次一样,但是上一次的 1 还在使用中;
2、在图中 ① 处,搜索的数也和上一次一样,但是上一次的 1 刚刚被撤销,正是因为刚被撤销,下面的搜索中还会使用到,因此会产生重复,剪掉的就应该是这样的分支。

if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
    continue;
}

数组中出现超过次数超过一半的数

class Solution {      // 摩尔投票法
    public int majorityElement(int[] nums) {
        if(nums.length==1){
            return nums[0];
        }
        int i=0;
        int result=0;
        for(int k=0;k<nums.length;k++){
            if(i==0){
                result= nums[k];       
            }
            if(nums[k]==result){
                i++;
            }else{
                i--;
            }   
        }
        return result;

    }
}

算法流程:
初始化: 票数统计 votes = 0, 众数 x;
循环抵消: 遍历数组 nums 中的每个数字 num ;
当 票数 votes等于 00 ,则假设 当前数字 num 为 众数 xx ;
当 num = x 时,票数 votes 自增 1 ;否则,票数 votes自减 1。
返回值: 返回 众数 x 即可。

最小的K个数

  • 计数排序法
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        //计数排序
        if(arr.length==0||k==0){
            return new int[0];
        }
        int[] counter = new int[10001];
        for(int num:arr){
            counter[num]++;
        }
        int[] res = new int[k];
        int x =0;
        for(int i =0;i<counter.length;i++){
            while(counter[i]-->0&&x<k){
                res[x++]=i;
            }
            if(x==k){
                break;
            }
        }
        return res;
    }
}
//使用优先队列  本题是求前 K 小,因此用一个容量为 K 的大根堆,
//每次 poll 出最大的数,那堆中保留的就是前 K 小啦(注意不是小根堆!小根堆的话需要把全部的元素都入堆,那是 O(NlogN)O(NlogN)😂,
//就不是 O(NlogK)O(NlogK)啦~~)
//因为 Java 中提供了现成的 PriorityQueue(默认小根堆),所以要重写比较器

 if(arr.length==0||k==0){
            return new int[0];
        }
        Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer t1,Integer t2){
                return t2-t1;
            }
        });
        int[] ans = new int[k];
        int m=0;
        for(int num:arr){
            if(m<k){
                queue.offer(num);
                m++;
            }else if(num<queue.peek()){
                queue.poll();
                queue.offer(num);
            }
        }
        int x=0;
        while(!queue.isEmpty()){
            ans[x++] = queue.poll();
        }
        return ans;
//使用数组自己实现堆
 if(arr.length==0||k==0){
        return new int[0];
    }
    int[] heap = new int[k];
    for(int i = 0;i<k;i++){
        heapInsert(heap,arr[i],i);
    }
    for(int i = k;i<arr.length;i++){
        if(arr[i]<heap[0]){
            heap[0] = arr[i];
            heapify(heap,0,k);
        }
    }
     return heap;
    }
    public void heapInsert(int[] heap,int val,int index){  //建堆
        heap[index] = val;  
        while(index!=0){
            int parent = (index-1)/2;
            if(heap[index]>heap[parent]){
                swap(heap,index,parent);
                index = parent;
            }else{
                break;
            }
        } 
    }
    public void heapify(int[] heap,int index, int len){  //调整堆
        int left = index*2+1;
        int right = index*2+2;
        int max = index;
        while(left<len){
            if(heap[left]>heap[index]){
                max = left;
            } 
            if(right<len&&heap[right]>heap[max]){
                max   = right;
            }    
            if(index!= max){
                swap(heap,index,max);
            }else{
                break;
            }
            index = max;
            left = index*2+1;
            right  =index*2+2;
        }
    }
    public void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
  • 快速选择
//快速排序要清楚!
if(arr.length==0||k==0){
            return new int[0];
        }
        partitionArray(arr,0,arr.length-1,k-1);
        int[] ans = new int[k];
        for(int i =0;i<k;i++){
            ans[i] = arr[i];
        }
        return ans;
    }
    public void partitionArray(int[] arr,int lo,int hi,int k){
        int  i  =partition(arr, lo, hi);
        if(k==i){
            return ;
        }else if(i>k){
            partitionArray(arr,lo,i-1,k);
        }else{
            partitionArray(arr,i+1,hi,k);
        }

    }
    public int partition(int[] arr,int lo,int hi){
        int v = arr[lo];
        int i=lo;
        int j =hi;
        while(i<j){
            while(i<j&&arr[j]>=v){
                j--;
            }
            while(i<j&&arr[i]<=v){
                i++;
            }
            if(i<j){
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j]  =temp;
            }
        }
        arr[lo]  = arr[i];
        arr[i] = v;
        return i;
    }

数据流中的中位数

//大顶堆 +小顶堆       
class MedianFinder {
    private    Queue<Double> maxHeap;
    private    Queue<Double>  minHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        maxHeap = new PriorityQueue<>(new Comparator<Double>(){
            public int compare(Double t1, Double t2){
                return (int)(t2-t1);
            }
        });
        minHeap = new PriorityQueue<>();
    }
    
    public void addNum(int num) {
        if(maxHeap.isEmpty()||num<=maxHeap.peek()){
            maxHeap.add((double)num);
        }else{
            minHeap.add((double)num);
        }
        modifyTwoHeaps();
    }
    
    public double findMedian() {
        if(maxHeap.isEmpty()){
            return 0.0;
        }
        if(maxHeap.size()==minHeap.size()){
            return (maxHeap.peek()+minHeap.peek())/2;
        }else{
            return maxHeap.size()>minHeap.size()?maxHeap.peek():minHeap.peek();
        }
    }
    private void modifyTwoHeaps(){
        if(maxHeap.size()==minHeap.size()+2){
            minHeap.add(maxHeap.poll());
        }
        if(minHeap.size()==maxHeap.size()+2){
            maxHeap.add(minHeap.poll());
        }
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

连续子数组的最大和

class Solution{          //动态规划
    public int maxSubArray(int[] nums) {
        if(nums==null||nums.length==0){
            return 0;
        }
        int cur=0;
        int res=Integer.MIN_VALUE;
        for(int i =0;i<nums.length;i++){
                cur += nums[i];
                res =Math.max(res,cur);
                cur = cur<0?0:cur;
        }
        return res;
    }
}

1-n中1出现的次数

class Solution {       //精简版
    public int countDigitOne(int n) {
       int count = 0;
        for(long k =1;k<=n;k*=10){
            long r = n/k;
            long m = n%k;
            count+=(r+8)/10*k+(r%10==1?m+1:0);
        }
        return count; 
    }
}
//初始版本 ,易于理解
public int countDigitOne(int n) {
    int count = 0;
    for (long k = 1; k <= n; k *= 10) {
        // xyzdabc
        int abc = n % k;
        int xyzd = n / k;
        int d = xyzd % 10;
        int xyz = (xyzd + 8) / 10;
        count += xyz * k;
        if (d == 1) {
            count += abc + 1;
        }
    }
    return count;
}
  • 类似题 : 阶乘中0的个数
     //出现5 因子的的个数
public int trailingZeroes(int n) {
    int count = 0;
    while (n > 0) {
        count += n / 5;
        n = n / 5;
    }
    return count;
}

两者都为数学规律题

第N个数字

class Solution {          // 也是数学规律题
    public int findNthDigit(int n) {
        long num =n;
        long max =  9;
        long size = 1;
        while(num>0){
            if(num-size*max>0){
                num=num-size*max;
                max *=10;
                size++;
            }else{
                long count = num/size;
                long  left = num%size;
                if(left==0){
                    return (int)(((long)(Math.pow(10,size-1))+count-1)%10);
                }else{
                    return (int)(((long)(Math.pow(10,size-1)+count)/(long)(Math.pow(10,size-left)))%10);
                }
            }
        }
        return 0;
    }
}

把数组排成最小的数

依据贪心的思想,越小的字符就排得越前
但是有一个问题,对于字符串的排序很难确定,因为长度不等,比较起来很繁琐
于是对于o1 o2,要比较它们,不妨比较o1+o2 o2+o1
通过相加,可以比较两个等长的字符串,从而确定哪个字符串具有的字符值较小。

class Solution {
    public String minNumber(int[] nums) {
        StringBuilder res = new StringBuilder();
        String[] strs = new String[nums.length];
        for(int i =0;i<nums.length;i++){
            strs[i] = String.valueOf(nums[i]);
        }
        Arrays.sort(strs,new Comparator<String>(){
            public int compare(String s1,String s2){
                if((s1+s2).compareTo(s2+s1)<0){
                    return -1;
                }else return 1;
            }   
        });
        for(String str:strs){
            res.append(str);
        }
        return res.toString();
    }
}

把数字翻译成字符串

class Solution {    //  加上注释的内容即便为0不可翻译版本 ,不加为0可以翻译的版本
    public int translateNum(int num) {
        String str  = String.valueOf(num);
        char[] chs = str.toCharArray();
       // int cur = chs[chs.length-1]=='0'?0:1;
       int cur = 1;
        int next =1 ;
        int temp = 0;
        for(int i =chs.length-2;i>=0;i--){
            // if(chs[i]=='0'){
            //     next = cur;
            //     cur = 0;
            // }else{
                temp = cur;
                if(chs[i]!='0'&&((chs[i]-'0')*10+(chs[i+1]-'0')<26)){
                    cur+= next;
                }
                next = temp;
          //  }
        }
        return cur;
    }
}

礼物的最大价值

回溯适用于遍历所有情况,而动态规划则是找最优情况

class Solution {     //普通回溯  递归  ,超时!
    int ans  = 0;
    int[][] directions ={{1,0},{0,1}};
    public int maxValue(int[][] grid) {
        boolean[][]  used = new boolean[grid.length][grid[0].length];
        int res = 0;
        process(grid,res,used,0,0);
        return ans;
    }
    public void process(int[][] grid,int res,boolean[][] used,int x,int y){
        if(x==grid.length-1&&y==grid[0].length-1){
            ans = Math.max(ans,res+grid[x][y]);
            return ;
        }
         if(check(x,y,grid)&&!used[x][y]){
             used[x][y] = true;
            for(int i = 0;i<=1;i++){
                int newX = x+ directions[i][0];
                int newY = y+ directions[i][1];
                process(grid,res+grid[x][y],used,newX,newY);
            }
            used[x][y] = false;
         }

    }
    public boolean check(int x,int y,int[][]grid){
        return x>=0&&x<grid.length&&y>=0&&y<grid[0].length;
    }
}
//动态规划
  for(int i =1;i<grid.length;i++){
            grid[i][0] +=grid[i-1][0];
        }
        for(int i =1;i<grid[0].length;i++){
            grid[0][i]+=grid[0][i-1];
        }
        for(int i = 1;i<grid.length;i++){
            for(int j =1;j<grid[0].length;j++){
                grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]);
            }
        }
        return grid[grid.length-1][grid[0].length-1];
    }
}

最长不含重复数字的子字符串

//滑动窗口一般套路
while(right<n){
	window.add() right++
	if(){
		window.remove() left++
	}
class Solution {   //滑动窗口   一般套路
    public int lengthOfLongestSubstring(String s) {
        if(s==null||s.length()<1){
            return 0;
        }
        if(s.length()==1){
            return 1;
        }
        int n = s.length();
        Set<Character> set = new HashSet<>();
        int i=0,j=0,ans =0;
        while(i<n&&j<n){
            if(!set.contains(s.charAt(j))){
                set.add(s.charAt(j++));
                ans = Math.max(ans,j-i);
            }else{
                set.remove(s.charAt(i++));
            }
        }
        return ans;
    }
}
//配合 hashmap  优化版本
public int lengthOfLongestSubstring(String s) {
        if(s==null||s.length()<1){
            return 0;
        }
        if(s.length()==1){
            return 1;
        }
        int n = s.length();
         HashMap<Character,Integer> map = new HashMap<>();
        int i=0,j=0,ans = 0;
        for(j=0;j<n;j++){
            if(map.containsKey(s.charAt(j))){
                i =Math.max(map.get(s.charAt(j)),i);    //防止i左移,abcdefcma 到第二个c的时候 i变成了 3,
                //但到第二个 a的时候如果取第一个a的位置,而不是a和当前i的最大值, i就变成了1,左侧索引左移
            }
            ans = Math.max(ans,j-i+1);
            map.put(s.charAt(j),j+1);
        }
        return ans;
    }
//还可以用数组来代替map,思路类似

丑数

class Solution {     动态规划
    public int nthUglyNumber(int n) {
        int[] dp = new int[n];
        dp[0] =1 ;
        int a =0,b=0,c=0;
        for(int i =1;i<n;i++){
            int n1 = dp[a]*2;
            int n2 = dp[b]*3;
            int n3 = dp[c]*5;
            dp[i] = Math.min(Math.min(n1,n2),n3);  
            if(dp[i]==n1){
                a++;
            }
            if(dp[i]==n2){
                b++;
            }
            if(dp[i]==n3){
                c++;
            }
        }
        return dp[n-1];
    }
}

第一个只出现一次的字符

class Solution {    //LinkedHashMap   也可以用HashMap,效率稍低
    public char firstUniqChar(String s) {
        if(s.length()==0){
            return ' ';
        }
        Map<Character,Boolean> map =  new LinkedHashMap<>();
        char[] chs = s.toCharArray();
        for(char ch:chs){
            map.put(ch,!map.containsKey(ch));
        }
        for(Map.Entry<Character,Boolean> a : map.entrySet()){
            if(a.getValue()){
                return  a.getKey();
            }
        }
        return ' ';
    }
}

数组中的逆序对

class Solution {                     
//基于归并排序
 //前一部分出队,还可以后一部分
    public int reversePairs(int[] nums) {
        int len = nums.length;
        if(len<2){
            return 0;
        }
        int[] temp  = new int[len];
        return  process( nums,0,len-1,temp);
    }
    public int process(int[] nums,int l,int r,int[] temp){
        if(l==r){
            return 0;
        }
        int mid = (l+r)/2;
        int left = process(nums,l,mid,temp);
        int right = process(nums,mid+1,r,temp);
        return left+right+merge(nums,l,mid,r,temp);
    }
    public int merge(int[] nums,int l, int mid,int r,int[] temp){
        for(int i =l;i<=r;i++){
            temp[i] = nums[i];
        }
        int i = l;
        int j =mid+1;
        int count = 0;
        for(int k =l;k<=r;k++){
            if(i>mid){
                nums[k] = temp[j];
                j++;
            }else if(j>r){
                nums[k] = temp[i];
                i++;
                count+= (r-mid);
            }else if(temp[j]>=temp[i]){
                nums[k] = temp[i];
                i++;
                count+=(j-mid-1);
            }else{
                nums[k] = temp[j];
                j++;
            }
        }
        return count;
    }
}

在这里插入图片描述
在这里插入图片描述
还有leetcode 315题 (困难) 类似
归并排序 + 索引数组 (要记住每个数的位置,不能更改数组,所以用索引数组,索引进行交换)

两个链表的第一个公共节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {    //快慢指针问题
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null){
            return null;
        }
        ListNode h1= headA;
        int l1=0;
        while(h1!=null){
            l1++;
            h1 = h1.next;
        }
        ListNode h2 = headB;
        int l2 = 0;
        while(h2!=null){
            l2++;
            h2 = h2.next;
        }
        if(l1>l2){
            for(int i = l1-l2;i>0;i--){
                headA = headA.next;
            }
        }
        if(l1<l2){
            for(int i = l2-l1;i>0;i--){
                headB = headB.next;
            }
        }
        while(headA!=null&&headB!=null){
            if(headB==headA){
                return headB;
            }
            headA = headA.next;
            headB = headB.next;
        } 
        return null; 
    }
}

还有一种思路: 两个同时遍历,到头后从另一方遍历, a+all+b = b+all+a

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值