读 剑指offer 有感-1

03[1]. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

限制:

2 <= n <= 100000

代码实现:

//方法一: 时间复杂度直接爆表,空间复杂度也会,
//  因为随着 数组的扩大, 空间复杂度会变大;然后就是时间复杂度,因为是 HashMap,可能后面会转为红黑树,那也要 O(nlog(n))
class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums.length<=0){
            return -1;
        }

        HashMap<Integer,Integer> hashMap = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if(hashMap.containsValue(nums[i])){
                return nums[i];
            }
            hashMap.put(i,nums[i]);
        }
        return -1;
    }
}

//方法二:
//首先是空间复杂度是 O(1) , 先排序了;
//其次是时间复杂度, 这里由于已经是排好序的了,所以时间复杂度是O(n)
class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums.length<=0){
            return -1;
        }

        Arrays.sort(nums);

        HashMap<Integer,Integer> hashMap = new HashMap<>();
        for (int i = 0; i <= nums.length-2; i++) {
            int j = i+1;
            if(nums[i]==nums[j]){
                return nums[i];
            }
        }
        return -1;
    }
}

//方法三:
//1.这里采用的是 哈希法,利用空间换时间,空间复杂度O(n),时间复杂度O(n)
// 就是让 nums数组的值, 对应哈希数组的下标, 如果出现了某一个下标,就让该下标对应的值 +1,值大于1的说明是重复的。
class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums.length<=0){
            return -1;
        }

        int[] arr = new int[nums.length];
        for(int i=0; i<nums.length;i++){
           	//就是让 nums数组的值, 对应哈希数组的下标, 
            //如果出现了某一个下标,就让该下标对应的值 +1,值大于1的说明是重复的。
            arr[nums[i]]++;
            if(arr[nums[i]]>1){
                return nums[i];
            }
        }
        return -1;
    }
}


//★牛批★ 鸽巢原理 方法四:
// 因为出现的元素值 < nums.length; 
// 所以我们可以将见到的元素 放到索引的位置,如果交换时,发现索引处已存在该元素则重复
// O(N) 空间O(1)
class Solution {
    public int findRepeatNumber(int[] nums) {
        if(nums.length<=0){
            return -1;
        }

        for(int i=0; i<nums.length;i++){
           //这里就是让 i 和 nums[i]比较,如果值不相等;
           while(nums[i]!=i){
               //然后再 比较 以nums[i]为下标, 比较 nums[nums[i]] 和 nums[i] 是否相等,
               //如果相等,说明:这里,在nums[i]为下标的位置处,已经有nums[nums[i]]==nums[i]
               //这里在比较相等,说明当前值nums[i] 和 nums[nums[i]] 重复,返回即可。
               if(nums[nums[i]] == nums[i])
                    return nums[i];
                // 如果不相等,那就让 下标nums[i] 和 下标i 上的值交换
              	 // 此时,是使得 下标为i上的值 和 以nums[i]为下标 在值上相等。
                int temp = nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;
           }
        }
        return -1;
    }
}

实例:
   数值:[2,3,4,1,2]
   下标:[0,1,2,3,4]
   i=0为例:
      从while开始
         0!=(nums[0]=2)
         (4=nums[2]) == 2
      不等,这里开始交换
      int temp = 2;
      num[0] = (num[temp]=num[2]=4)
      (num[temp]=num[2])= 2

03[2].不修改数组找出重复的数字

​ 在一个长度为n+1的数组里的呃所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如:如果输入长度为8的数组{2,3,5,4,3,2,6,7}那么对应的输入都是重复的数字2或者3。

代码实现:

public class DuplicationSearch {
    public static void main(String[] args) {
        int[] nums = {2,3,5,4,3,2,6,7};
        int[] nums1 = {6,6,6,6,6,6,6,6};
        System.out.println(getDuplication(nums1,nums1.length));
    }

    public static int getDuplication(int[] nums,int length){
        if(nums==null || length<=0){
            return -1;
        }

        //这个 start、end是该数组的取值范围
        int start = 1;
        int end = length-1;
        while(end>=start){

            //这个 +start用的是真的巧妙
            int middle = ((end-start)>>1) + start;

            //看清,这里是middle
            int count = countRange(nums,length,start,middle);
            //二分法最终会走到 start == end
            if(start==end){
                if(count>1){
                    return start;
                }else {
                    break;
                }
            }
            /**
             * 如果count的值比
             */
            if(count>((middle-start)+1)){
                end = middle;
            }else {
                start = middle+1;
            }
        }
        return -1;
    }

    public static int countRange(int[] numbers,int length,int start,int end){
        if(numbers==null){
            return -1;
        }
        int count = 0;
        for (int i = 0; i < length; i++) {
            /**
             * 这个比较的就是 numbers数组中的值 在 start~end范围内的数有多少个
             */
            if(numbers[i]>=start && numbers[i]<=end){
                count++;
            }
        }
        return count;
    }
}

04. 二维数组中的查找

​ 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

给定 target = 5,返回 true

给定 target = 20,返回 false

限制:

0 <= n <= 1000
0 <= m <= 1000

代码实现:

/**
  解题思路:
      ● 若数组为空,返回 false
      ● 初始化行下标为 0,列下标为二维数组的列数减 1
      ● 重复下列步骤,直到行下标或列下标超出边界
         ○ 获得当前下标位置的元素 num
         ○ 如果 num 和 target 相等,返回 true
         ○ 如果 num 大于 target,列下标减 1
         ○ 如果 num 小于 target,行下标加 1
      ● 循环体执行完毕仍未找到元素等于 target ,说明不存在这样的元素,返回 false`
*/
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {

        if(matrix.length==0){
            return false;
        }

        int i = 0;
        int j = matrix[0].length-1;

        while(i<matrix.length && j>=0){
            if(target == matrix[i][j]){
                return true;
            }else if(target < matrix[i][j]){
                j--;
            }else{
                i++;
            }
        }     
        return false;  
    }
}

05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."

限制:

0 <= s 的长度 <= 10000

代码实现:

//方法一:
class Solution {
    public String replaceSpace(String s) {
        if(s.length() == 0){
            return "";
        }
        return s.replaceAll(" ","%20");
    }
}

//方法二:
// 占内存的情况,这两种方法基本一样;
// 在时间消耗上:方法一:2ms,  而方法二:直接0ms
class Solution {
    public String replaceSpace(String s) {
        if(s.length() == 0){
            return "";
        }
        
        char[] charArr = s.toCharArray();
        StringBuffer bf = new StringBuffer();
        for(int i=0;i<s.length();i++){
            if(charArr[i]==' '){
                bf.append("%20");
            }else{
                bf.append(charArr[i]);
            }
           
        }
        return bf.toString();
    }
}

06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

限制:

0 <= 链表长度 <= 10000

代码实现:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//感觉不论怎么搞,空间复杂度是降不下来的。
//方法一:
class Solution {
    public int[] reversePrint(ListNode head) {
        if(head==null){
            return new int[0];
        }

        ArrayList<Integer> list = new ArrayList<>();
        while (head!=null){
            list.add(head.val);
            head = head.next;
        }
        int[] arr = new int[list.size()];
        int length = list.size()-1;
        for(int i=0; i <= list.size()-1;i++){
            arr[i] = list.get(length--);
        }
        return arr;
    }
}

//方法二:利用栈,操作,但是实际运行效果还没有上面的效果要好,这个 3ms,上面1ms,内存占用基本大小一样
class Solution {
    public int[] reversePrint(ListNode head) {
        if(head==null){
            return new int[0];
        }

        Stack<Integer> stack = new Stack<Integer>();
        while (head!=null){
            stack.push(head.val);
            head = head.next;
        }

        int[] arr = new int[stack.size()];
        int length = stack.size();
        for (int i = 0; i < length ; i++) {
            arr[i] = stack.pop();
        }
        return arr;
    }
}

07. 重建二叉树

已知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;
已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;
但是已知前序遍历序列和后序遍历序列,是不能确定一棵二叉树的;
即:没有中序遍历序列的情况下是无法确定一颗二叉树的。

​ 输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出:

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

限制:

0 <= 节点个数 <= 5000

代码实现:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int preStart = 0;
        int preEnd = preorder.length-1;
        int inStart = 0;
        int inEnd = inorder.length-1;
        return reBuildTree(preorder,preStart,preEnd,inorder,inStart,inEnd);
    }

    public TreeNode reBuildTree(int[] preorder,int preStart,int preEnd, 
                                int[] inorder,int inStart,int inEnd){
        if(preStart>preEnd || inStart>inEnd){
            return null;
        }
        //这里是是preStart,每一次递归过来 new的是当前节点
        TreeNode root = new TreeNode(preorder[preStart]);
         for(int i = inStart ; i<=inEnd ; i++){
            if(preorder[preStart] == inorder[i]){
                //这里preStart+(i-inStart) :preStart加的是 inStart到 i的距离
                root.left = reBuildTree(preorder,preStart+1,preStart+i-inStart,
                                        inorder,inStart,i-1);
                //preStart+i-inStart+1 ,刚好就是preStart+(i-inStart)+1
                root.right = reBuildTree(preorder,preStart+i-inStart+1,preEnd,
                                         inorder,i+1,inEnd);
                break;
            }
        }
        return root;
    }
}

输入:
   [3,9,20,15,7]
   [9,3,15,20,7]
输出
   [3,9,20,null,null,15,7]
预期结果
   [3,9,20,null,null,15,7]
  • 中序遍历和后序遍历:

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
       int preStart = 0;
       int preEnd = preorder.length-1;
       int inStart = 0;
       int inEnd = inorder.length-1;
       return reInAndAfterBuildTree(preorder,preStart,preEnd,inorder,inStart,inEnd);
    }
    
    /**
      * 中序和后续
      * @return
      */
    public static TreeNode reInAndAfterBuildTree(int[] afterOrder,
                                                 int afterStart,int afterEnd,
                                                 int[] inorder,
                                                 int inStart,int inEnd){
       // 防止前后交叉导致已选过的数值重复选中
       if(afterStart>afterEnd || inStart>inEnd){
          return null;
       }
       // 防止数组越界
       if (afterStart < 0 || afterEnd < 0 || inStart < 0 || inEnd < 0) {
          return null;
       }
       
       TreeNode root = new TreeNode(afterOrder[afterEnd]);
    	
       int index = indexOfRootInMid(afterOrder[afterEnd],inorder);
       // 所选节点左子树的数量
       int lefNum = index - inStart;
       root.left = reInAndAfterBuildTree(afterOrder,afterStart,afterStart+lefNum-1,
                                         inorder,inStart,index-1);
       root.right = reInAndAfterBuildTree(afterOrder,afterStart+lefNum,afterEnd-1,
                                          inorder,index+1,inEnd);
    
       return root;
    }
    
    // 获得所选子树的根节点在中序数组中的下标
    public static int indexOfRootInMid(int afterRoot , int[] inOrder){
       for (int i = 0; i < inOrder.length; i++) {
          if(afterRoot == inOrder[i]){
             return i;
          }
       }
       return -1;
    }
    
    //后续遍历
    public static void afterOrder(TreeNode root){
       if(root==null){
          return;
       }
       afterOrder(root.left);
       afterOrder(root.right);
       System.out.print(root.val+"\t");
    }
    
    
    public static void main(String[] args) {
       int[] a = {4,6,7,5,2,9,8,3,1};
       int[] b = {4,2,6,5,7,1,3,9,8};
       TreeNode node = buildTree(a, b);
       afterOrder(node);
    }
    
    //运行结果:
    	4	6	7	5	2	9	8	3	1
    

08. 二叉树的下一个节点

​ 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

分析:

​ 就是有一颗二叉树,然后给了一个结点,按中序遍历,输入这个结点的下一个结点。

实例:

				1
			 /   \	
		   2	   3
		 /  \	   /  \
      4    5  6    7
      	 / \
     		8   9
分为两种旅客

代码实现:

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
   
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null){
            return null;
        }
        
        if(pNode.right!=null){  //如果有右子树,则找右子树的最左节点
            pNode = pNode.right;
            while(pNode.left!=null){
                pNode = pNode.left;
            }
            return pNode;
        }
        
        while(pNode.next!=null){  //没右子树,则找第一个当前节点是父节点左孩子的节点
            if(pNode.next.left==pNode){
                return pNode.next;
            }
            pNode = pNode.next;
        }
        
        return null;  //退到了根节点仍没找到,则返回null
    }
   
    public static void main(String[] args) {
        TreeLinkNode root = new TreeLinkNode(1);
        root.left = new TreeLinkNode(2);
        root.right = new TreeLinkNode(3);
        root.left.next = root;
        root.right.next = root;

        root.left.left = new TreeLinkNode(4);
        root.left.right = new TreeLinkNode(5);
        root.left.left.next = root.left;
        root.left.right.next = root.left;

        root.right.left = new TreeLinkNode(6);
        root.right.right = new TreeLinkNode(7);
        root.right.left.next = root.right;
        root.right.right.next = root.right;

        root.left.right.left = new TreeLinkNode(8);
        root.left.right.right = new TreeLinkNode(9);
        root.left.right.left.next = root.left.right;
        root.left.right.right.next = root.left.right;

        System.out.println(GetNext(root.left.right.right));
    }
}

09[1]. 用两个栈实现队列

​ 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

示例 2:

输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

提示:

  • 1 <= values <= 10000
  • 最多会对 appendTail、deleteHead 进行 10000 次调用

代码实现:

//这个题目也给了一个思路:
//1° LinkedList 的 push 和 pop 组合是一个栈;
//2° LinkedList 的 add 和 pop 组合是一个队列;
class CQueue {
    LinkedList<Integer> stack1;
    LinkedList<Integer> stack2;

    public CQueue() {
        stack1 = new LinkedList<>();
        stack2 = new LinkedList<>();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if(stack2.isEmpty()){
            if(stack1.isEmpty()){
                return -1;
            }
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }else{
            return stack2.pop();
        }
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

09[2]. 用两个队列实现栈

代码实现:

/**
 * 两个队列做一个栈:
 * 添加:哪个队列有元素,往哪个队列中添加;
 * 删除:哪个队列有元素,就将该队列中的元素全部移动到另外一个队列,只保留了一个元素,该元素就是要删除的元素。
 */
public class DoubleQueueDoStack {
    static Queue<Integer> queue1;
    static Queue<Integer> queue2;

    public DoubleQueueDoStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    /**
     * 添加的时候:
     * 如果两个队列都为空,往哪个里面插入都可以;
     * 如果队列不为空,那两个队列也只能是一个队列为空、一个队列不为空
     * @param value
     * @return
     */
    public boolean appendTail(int value) {
        if (!queue1.isEmpty()) {
            return queue1.offer(value);
        } else {
            return queue2.offer(value);
        }
    }

    /**
     * 删除:一定是删除 移动队列元素后,剩下一个元素的队列删除
     * @return
     */
    public int deleteHead() {
        if(!queue1.isEmpty() && queue2.isEmpty()){
            while (queue1.size()!=1){
                queue2.offer(queue1.poll());
            }
            return queue1.poll();
        }

        if(queue1.isEmpty() && !queue2.isEmpty()){
            while (queue2.size()!=1){
                queue1.add(queue2.poll());
            }
            return queue2.poll();
        }
        return -1;
    }

    public static void main(String[] args) {
        DoubleQueueDoStack doStack = new DoubleQueueDoStack();
        doStack.appendTail(1);
        doStack.appendTail(2);
        doStack.appendTail(3);
        doStack.appendTail(4);
        System.out.println("----------------");
        System.out.println("queue2:"+queue2.toString());

        System.out.println("---------删除-------");
        doStack.deleteHead();
        System.out.println("queue1:"+queue1.toString());
        System.out.println("queue2:"+queue2.toString());

        System.out.println("---------添加-------");
        doStack.appendTail(5);
        System.out.println("queue1:"+queue1.toString());
        System.out.println("queue2:"+queue2.toString());

        System.out.println("---------删除-------");
        doStack.deleteHead();
        System.out.println("queue1:"+queue1.toString());
        System.out.println("queue2:"+queue2.toString());
    }
  
}

//运行结果:
      ----------------
      queue2:[1, 2, 3, 4]
      ---------删除-------
      queue1:[1, 2, 3]
      queue2:[]
      ---------添加-------
      queue1:[1, 2, 3, 5]
      queue2:[]
      ---------删除-------
      queue1:[]
      queue2:[1, 2, 3]

10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

代码实现:

//牛批
class Solution {
    public int fib(int n) {
        int a = 0, b = 1, sum;
        for(int i = 0; i < n; i++){
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

//这种方式应该也行,就是超出时间限制了 。
class Solution {
    public int fib(int n) {
        if(n==0){
            return 0;
        }
        if(n==1){
            return 1;
        }
        return (fib(n-1)+fib(n-2))%1000000007;
    }
}

10- II. 青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2

示例 2:

输入:n = 7
输出:21

示例 3:

输入:n = 0
输出:1

提示:

  • 0 <= n <= 100

运行结果:

class Solution {
    public int numWays(int n) {
        int a=1,b=1,sum;
        for(int i=0;i<n;i++){
            sum = (a+b)%1000000007;
            a = b;
            b = sum;
        }
        return a;
    }
}

10-Ⅲ. 变态青蛙跳

一只青蛙一次可以跳上1级台阶,也可以跳上2级… 它也可以跳上n级台阶,此时该青蛙跳上一个n级的台阶总共有多少中跳法。

分析:

1° 每一个台阶都有跳与不跳两种(除了最后一个台阶),最后一个台阶必须跳。

​ ∴ f(n) = 2的n-1次方

2° ∵ n个台阶,第一步有n中跳法:跳1阶,跳两阶… 跳n阶;

​ 跳1阶,剩下n-1阶,则剩下跳法f(n-1);

​ 跳2阶,剩下n-2阶,则剩下跳法f(n-2);

​ ∴ f(n) = f(n-1) + f(n-2) + … + f(1);

​ f(n-1) = f(n-2) + … + f(1);

​ ∴ f(n) = 2*f(n-1);

​ = 2的n-1次方 * f(1);

​ =2的n-1次方

代码实现:

public class Solution {
    public int JumpFloorII(int target) {
        if(target<=0){
            return -1;
        }
        return new Double(Math.pow(2,target-1)).intValue();
    }
}

//这里求n次方,可以用 16题的 数值的整次方的方式求解

11[1]. 旋转数组的最小数字

​ 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

代码实现:

//方法一:最笨方法,部分有序,
//顺序找到一个小于当前元素的下一个元素即可。
//如果不是,那就返回第一个元素
class Solution {
    public int minArray(int[] numbers) {
        if(numbers.length==0){
            return -1;
        }
        if(numbers.length==1){
            return numbers[0];
        }

        int i,j=1;
        for(i=0;i<numbers.length;i++){
            //这里需要注意,j的值是比i的大1
            if(j<numbers.length && numbers[i]>numbers[j]){
                return numbers[j];
            }
            j++;
        }
        //如果不是,那就返回第一个元素
        return numbers[0];
    }
}

//方法二:夹逼法
class Solution {
    public int minArray(int[] numbers) {
        if(numbers.length==0){
            return -1;
        }
        if(numbers.length==1){
            return numbers[0];
        }
		  //这里对特殊情况进行输出,如果没有 “旋转”,那么只用比较 第一个和最后一个元素的大小即可(数组本身就是升序的。)。
        if(numbers[0]<numbers[numbers.length-1]){
            return numbers[0];
        }

        //★这里夹逼比较★
        int i=0,j=numbers.length-1;
        while(i<=j){
            if(numbers[i]<=numbers[j]){
                j--;
            }else if(numbers[i]>numbers[j]){
                i++;
            }
        }
        
        return numbers[i];
    }
}


//方法三:分治(减治,分治的特殊情况), 因为分治只有在排好序之后才能分治
   >>  这个是带符号右移
   >>> 这个是无符号右移
public class Solution {

    // [3, 4, 5, 1, 2]
    // [1, 2, 3, 4, 5]
    // 不能使用左边数与中间数比较,这种做法不能有效地减治

    // [1, 2, 3, 4, 5]
    // [3, 4, 5, 1, 2]
    // [2, 3, 4, 5 ,1]

    public int minArray(int[] numbers) {
        int len = numbers.length;
        if (len == 0) {
            return 0;
        }
        int left = 0;
        int right = len - 1;
        /**
        		这个while处理的是旋转之后的数组;
        		如果没有旋转说明有序,直接返回numbers[left]即可。
        */
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (numbers[mid] > numbers[right]) {
                // [3, 4, 5, 1, 2],mid 以及 mid 的左边一定不是最小数字
                // 下一轮搜索区间是 [mid + 1, right]
                left = mid + 1;
            } else if (numbers[mid] == numbers[right]) {
                // 只能把 right 排除掉,下一轮搜索区间是 [left, right - 1]
                right = right - 1;
            } else {
                // 此时 numbers[mid] < numbers[right]
                // mid 的右边一定不是最小数字,mid 有可能是,下一轮搜索区间是 [left, mid]
                right = mid;
            }
        }

        // 最小数字一定在数组中,因此不用后处理
        return numbers[left];
    }
}


//方法四:也有一个比较狗的方法
直接使用Array.sort(numbers),排序之后直接取第一个 numbers[0]

11[2].员工按年龄排序

对公司的所有员工按年龄排序,员工大约有几万人。

要求:时间复杂度O(N),可以使用辅助空间。

代码实现:

public class SortAges {

    static int[] sortAges(int ages[],int length){
        if(ages.length<0){
            return new int[0];
        }
        //辅助空间,年龄范围
        int[] helpSpace = new int[100];
        for (int i = 0; i < ages.length; i++) {
            if(0<ages[i] && ages[i]<100){
                helpSpace[ages[i]]++;
            }else {
                System.out.println("这里可以抛一个异常,超出年龄范围!");
            }
        }

        int index = 0;
        for (int i = 0; i < helpSpace.length; i++) {
            for (int j = 0; j < helpSpace[i]; j++) {
                ages[index] = i;
                index++;
            }
        }
        return ages;
    }

    public static void main(String[] args) {
        int[] ages = {1,3,1,5,10,50,12,11,30,50,12,10,44,34,44};
        System.out.println(Arrays.toString(sortAges(ages, ages.length)));
    }
}

运行结果:
	[1, 1, 3, 5, 10, 10, 11, 12, 12, 30, 34, 44, 44, 50, 50]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值