剑指offer-每日6题之第五天(java版)

原题链接:

 第一题:复杂链表的复制
 第二题:二叉树和双向链表
 第三题:字符串的排列
 第四题:数组中出现次数超过一半的数字
 第五题:最小的K个数
 第六题:连续子数组的最大和

第一题:复杂链表的复制

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

节点定义为:

public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}

解析

分为三步:

       ①复制链表:nodea->nodea1->nodeb->nodeb1

       ②复制链表的random指针

       ③拆分链表

public class Solution {
    public RandomListNode Clone(RandomListNode pHead){  
        if(pHead==null)return null;
        RandomListNode pcur=pHead;
         //第一步
        while(pcur!=null){
            RandomListNode node = new RandomListNode(pcur.label);
            node.next=pcur.next;//先把当前节点的下一个节点给复制节点的下一个
            pcur.next=node;//当前节点的下一个是node
            pcur=node.next;//当前节点指向复制节点的下一个
        }
        //第二步
        pcur=pHead;
        while(pcur!=null){
            if(pcur.random!=null){
                 RandomListNode random=pcur.random;
                 pcur.next.random=random.next;
            }
            pcur=pcur.next.next;  
        }
      //第三步
      RandomListNode head=pHead.next;
      RandomListNode cur=head;
      pcur=pHead;
      while(pcur!=null){
         pcur.next=pcur.next.next;
         if(cur.next!=null)
              cur.next=cur.next.next;
         cur=cur.next;
         pcur=pcur.next;
      }
      return head;
        
    }
}

第二题:二叉树和双向链表

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解析

解法一:递归遍历,类似中序遍历

将二叉树的左节点转换为双向链表的前指针,右节点转换为后指针

先遍历左子树,然后把根连在左子树后面,最后把右子树遍历完加在根的后面

public class Solution {
        TreeNode pre=null;
        TreeNode head=null;
    public TreeNode Convert(TreeNode pRootOfTree) {
         if(pRootOfTree==null)return null;
         Convert( pRootOfTree.left);
         if(pre==null){
            pre=pRootOfTree;
            head=pRootOfTree;
         }else{
            pre.right=pRootOfTree;
            pRootOfTree.left=pre;
            pre=pRootOfTree;
         }
         Convert( pRootOfTree.right);
         return head;
    }
}

方法二:类似于非递归的中序遍历

根据递归,建立栈结构,找到最左的节点,修改当前遍历节点与前一遍历节点的指针指向

public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null)return null;
        Stack<TreeNode> stack=new Stack<TreeNode>();
        TreeNode pre=null;
        TreeNode pcur=pRootOfTree;
        boolean isFirst=true;
        while(!(pcur==null && stack.isEmpty())){

            //找到最左的节点
            while(pcur!=null){
                stack.push(pcur);
                pcur=pcur.left;
            }
          
           //对当前节点设置
            pcur=stack.pop();
            if(isFirst){
                pRootOfTree=pcur;//将最左的节点计为链表的头节点返回
                pre=pRootOfTree;  
                isFirst=false;
            }else{
                pre.right=pcur;
                pcur.left=pre;
                pre=pcur;
            }

           //遍历右节点
            pcur=pcur.right;
        }
        return pRootOfTree;
        
    }
}

第三题:字符串的排列

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4

解析

递归:

        eg:         [abc]

                /         |          \

          a[bc]    b[ac]      c[ba]      第一个a和后面的每个单词交换,得到的新的字符串递归到后俩个单词(每次交换完,递归后要换来)

           /  \          /   \          /     \   

       abc acb  bac bca  cba  cab

import java.util.ArrayList;
import java.util.Collections;
public class Solution {
    public ArrayList<String> Permutation(String str) {
         ArrayList<String> res=new  ArrayList<String> ();
       if(str==null)return res;
        char[] chs=str.toCharArray();
        Permutation(chs,res,0);
        Collections.sort(res);
        return res;
    }
     public void Permutation(char [] chs,ArrayList<String> list,int index){
         if(chs.length==0||chs==null)return;
         if(index==chs.length){
             String str=String.valueOf(chs);
             if(list.contains(str))return ;
             else list.add(str);
         }else{
             for(int i=index;i<chs.length;i++){
                 swap(i,index,chs);
                 Permutation(chs,list,index+1);
                 swap(i,index,chs);
             }
         }
     }
    public void swap(int i,int j,char[]chs){
        char temp=chs[i];
        chs[i]=chs[j];
        chs[j]=temp;
    }
}

第四题:数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解析

第一个数字作为第一个士兵,守阵地;temp = 1;
   遇到相同元素,temp++;
   遇到不相同元素,即为敌人,同归于尽,temp--;当遇到temp为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在    阵地上的士兵,有可能是主元素。
   再加一次循环,记录这个士兵的个数看是否大于数组

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null||array.length==0)return 0;
        int temp=1;
        int r=array[0];
        for(int i=1;i<array.length;i++){
            if(temp==0){
               r=array[i];
               temp=1;  
            }else if(r==array[i])
                temp++;
            else temp--;  
        }
        temp=0;
        for(int i=0;i<array.length;i++){
            if(array[i]==r)temp++;
        }
        return (temp > array.length/2) ? r: 0;
    }
}

第五题:最小的K个数

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解析

方法一:

       定义一个list集合,集合里先存数组的前k个数据,然后后面的数据和和集合里最大的数据进行比较,如果当前数据比list最大的数据小,更新list集合

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
     ArrayList<Integer> list = new ArrayList<Integer>();
        if(input.length < k || k == 0)
            return list;
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        for (int i = k; i < input.length; i++) {
            int j = this.getMax(list);
            int temp = (Integer) list.get(j);
            if (input[i] < temp)
                list.set(j, input[i]);
        }
        return list;
    }
    public int getMax(ArrayList<Integer> list) {
        int max = list.get(0);
        int j = 0;
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i) > max) {
                max = list.get(i);
                j = i;
            }
        }
        return j;
    }
}

解析

方法二:

   基于Partation快速排序:

         如果Partation得到的结果是k-1,说明k前面有k个最小的,直接返回

         如果结果小于k-1,递归到Partation得到的结果后面(要得到k个小的数,pivot肯定在结果后面)

         否则,递归到递归到Partation得到的结果前面(要得到k个小的数,pivot肯定在结果前面)

  Partation中选择的pivot是开始位置的数据

import java.util.*;
public class Solution {
        public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
       if(input.length<k||k==0)       return list;
        findKMin(input,0,input.length-1,k);
        for(int i = 0; i < k; i++ ) 
              list.add(input[i]);
        return list;
        }
        public void findKMin(int[] a, int begin, int end,int k){
        if(begin<end){
            int pos=partition(a,begin,end);
            if(pos==k-1)return;
            else if(pos<k-1)findKMin(a,pos+1,end,k);
            else findKMin(a,begin,pos-1,k);
        }
        }
        public int partition(int []a,int begin,int end){
        int pivot=a[begin];
        while(begin<end){
            while(begin<end && a[end]>=pivot)end--;
            a[begin]=a[end];
            while(begin<end && a[begin]<=pivot)begin++;
            a[end]=a[begin];
        }
        a[begin]=pivot;
        return begin;
        }
}

解析

用最大堆保存这k个数,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
       //用优先队列建立大根堆,堆顶元素为最大元素
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });

           for (int i = 0; i < length; i++) {
            if (maxHeap.size() <k) {//当堆中的数据小于k个,直接插入堆
                maxHeap.offer(input[i]);
            } else if (maxHeap.peek() > input[i]) {//堆顶大于当前数据,堆顶元素替换为当前元素
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }

        for (Integer integer : maxHeap) {
            result.add(integer);
        }
        return result;
    }
}

六题:连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

解析

定义sum和max,max记录最大值

sum记录子串和,如果sum<0,记录sum从当前位置开始,否则,把当前的数据加到sum中

每次将max和sum进行比较,持续更新max的值

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null||array.length==0)return 0;
        int sum=0,max=Integer.MIN_VALUE;
        for(int i=0;i<array.length;i++){
            if(sum<=0)sum=array[i];
            else sum+=array[i];
            if(sum>max)
                max=sum;
        }
        return max;
    }
}

猜你喜欢

转载自blog.csdn.net/ccccc1997/article/details/81295715