剑指offer:复杂链表的复制、二叉搜索树与双向链表、序列化二叉树、字符串排列、数组中出现次数超过一半的数字、最小的k个数、数据流中的中位数、连续子数组的最大和、1~n整数中1出现的次数

复杂链表的复制

题目

在这里插入图片描述

题解

思路:使用哈希表,key为原节点的引用,value为新节点的引用

代码:

 public Node copyRandomList(Node head) {
        Map<Node,Node> map=new HashMap<>();
        Node cur=head;
        while(cur!=null){
            Node node=new Node(cur.val);
            map.put(cur,node);
            cur=cur.next;
        }
        cur=head;
        while(cur!=null){
            map.get(cur).next=map.get(cur.next);
            map.get(cur).random=map.get(cur.random);
            cur=cur.next;
        }
        return map.get(head);
    }

二叉搜索树与双向链表

题目

在这里插入图片描述

题解

代码:

    public Node pre=null;
    public Node head=null;
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;
         inOrder(root);
         head.left=pre;
         pre.right=head;
        return head;
    }
    public void inOrder(Node cur){
         if(cur==null) return;
         inOrder(cur.left);
         cur.left=pre;
         if(pre!=null){
             pre.right=cur;
         }else {
             head=cur;
         }
         pre=cur;
         inOrder(cur.right);
    }

序列化二叉树

题目

在这里插入图片描述

题解

思路:采用先序遍历的方式对树进行序列化,遇到空的节点用None来代替,在反序列化时也是采用先序遍历的方式,遇到None就代表当前节点为空,否则构造其左右子树

代码:

 public String serialize(TreeNode root) {
       String ret="";
       return serializeChild(root,ret);
    }
    public String serializeChild(TreeNode root, String str){
        if(root==null){
            str+="None,";
        }else {
            str+=String.valueOf(root.val)+",";
            str=serializeChild(root.left,str);
            str=serializeChild(root.right,str);
        }
        return str;
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
         String[] ss=data.split(",");
         List<String> list=new ArrayList<>(Arrays.asList(ss));
         return deserializeChild(list);
    }
    public TreeNode deserializeChild(List<String> list){
         if(list.get(0).equals("None")){
             list.remove(0);
             return null;
         }
         TreeNode root=new TreeNode(Integer.valueOf(list.get(0)));
         list.remove(0);
         root.left=deserializeChild(list);
         root.right=deserializeChild(list);
         return root;
    } 

字符串排列

题目

在这里插入图片描述

题解

代码:

   List<String> ret;
     boolean[] vis;
    public String[] permutation(String s) {
         ret=new ArrayList<>();
         char[] str=s.toCharArray();
         Arrays.sort(str);
         int n=str.length;
         vis=new boolean[n];
         StringBuffer sb=new StringBuffer();
          dfs(str,0,n,sb);
          String[] res=new String[ret.size()];
          for(int i=0;i<res.length;i++){
              res[i]=ret.get(i);
          }
          return res;
    }
    public void dfs(char[] str,int i,int n,StringBuffer sb){
          if(i==n){
              ret.add(sb.toString());
              return;
          }
          for(int j=0;j<n;j++){
              if(vis[j] || (j>0 && !vis[j-1] && str[j-1]==str[j])){
                  continue;
              }
              vis[j]=true;
              sb.append(str[j]);
              dfs(str,i+1,n,sb);
              sb.deleteCharAt(sb.length()-1);
              vis[j]=false;
          }
    }

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

题目

在这里插入图片描述

题解

代码:

 public int majorityElement(int[] nums) {
        
        int hp=nums[0];
        int count=1;
        for(int i=1;i<nums.length;i++){
            if(nums[i]==hp){
                count++;
            }else if(count==0){
                hp=nums[i];
                count=1;
            }else{
                count--;
            }
        }
        return hp;
    }

最小的k个数

题目

在这里插入图片描述

题解

思路:基于快排的思想,经过依次划分后,会得到一个基准arr[i],基准的左侧区间为[l,i-1],基准的右侧区间为[i+1,r],判断k与i的关系

  • 如果k==i,说明arr[i]是第k+1个数,直接返回数组前k个数即可
  • 如果k<i,说明第k+1个数在左区间中,递归排序左区间
  • 如果k>i,说明第k+1个数在右区间中,递归排序右区间

代码:

 public int[] getLeastNumbers(int[] arr, int k) {
        
        if(k>=arr.length) return arr;
         return quickSort(arr,0,arr.length-1,k);
    }
    private int[] quickSort(int[] arr,int l,int r,int k){
           int i=l;
           int j=r;
           while(i<j){
               while(i<j && arr[j]>=arr[l]) j--;
               while(i<j && arr[i]<=arr[l]) i++;
               swap(arr,i,j);
           }
           swap(arr,i,l);

            if(i>k) return quickSort(arr,l,i-1,k);
            if(i<k) return quickSort(arr,i+1,r,k);
            return Arrays.copyOf(arr,k);

    }
    private void swap(int[] arr,int i,int j){
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

数据流中的中位数

题目

在这里插入图片描述

题解

具体步骤:

step1:创建大根堆用来维护数组中较小的一半元素,创建小根堆用来维护数组中较大的一半元素
step2:当数组元素个数为奇数个时,两个堆的元素数量一定是相差1的,于是我们规定让大根堆中多存储一个元素,因此当数组个数为奇数个时,中位数就是大根堆的堆顶元素
step3:先将数据流中的数存入大根堆,然后将大根堆的堆顶元素弹出放到小根堆中,这样就保证了大根堆中始终存储的是数组中较小的一半元素,小根堆中存储的是较大的一般元素,在step2中规定大根堆中元素应该是大于等于小根堆中的元素的,因此因此需要再比较二者的长度,若是小顶堆长度小于大顶堆,需要从大顶堆中弹出最小值到大顶堆中进行平衡
step4:取中位数时,如果大根堆的元素个数比小根堆的元素多,说明一共有奇数个元素,直接返回大根堆的堆顶元素即可,如果两个堆中的元素个数相等,返回两个堆顶元素的平均值

  private PriorityQueue<Integer> minQ;//大根堆,存储较小的一半元素
      private PriorityQueue<Integer> maxQ;//小根堆,存储较大的一半元素
    public MedianFinder() {
             minQ=new PriorityQueue<>((a,b)->(b-a));
             maxQ=new PriorityQueue<>();
    }
    
    public void addNum(int num) {
        minQ.add(num);
        maxQ.add(minQ.poll());
        if(minQ.size()<maxQ.size()){
            minQ.add(maxQ.poll());
        }
    }
    
    public double findMedian() {
         if(minQ.size()>maxQ.size()){
             return 1.0*minQ.peek();
         }else {
             return (minQ.peek()+maxQ.peek())/2.0;
         }
    }

连续子数组的最大和

题目

在这里插入图片描述

题解

代码:

  public int maxSubArray(int[] nums) {
         
         int pre=nums[0];
         int ret=nums[0];
         for(int i=1;i<nums.length;i++){
             int sum=Math.max(pre+nums[i],nums[i]);
             ret=Math.max(sum,ret);
             pre=sum;
         }
         return ret;
    }

1~n整数中1出现的次数

题目

在这里插入图片描述

题解

在这里插入图片描述

代码:

    public int countDigitOne(int n) {
       if(n<1) return 0;
       int cur=n%10;
       int low=0;
       int high=n/10;
       int base=1;
       int ret=0;
       while(cur!=0 || high!=0){
           if(cur>1){
               ret+=(high+1)*base;
           }else if(cur==1){
               ret+=high*base+low+1;
           }else if(cur<1){
               ret+=high*base;
           }
           low+=cur*base;
           base*=10;
           cur=high%10;
           high=high/10;
       }
       return ret;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值