剑指Offer数组中出现超过一半的数字+最小的k个数

一.快速排序

首先介绍一下快速排序,因为这两题都可以使用快速排序的Partition函数来做。快速排序算法的关键是在数组中选择一个数字假设为pivot,然后将数组分为两部分,小于该数字的方法该数字前面,大于该数字的放到该数字的后面。这个函数的实现是通过设置两个指针i和j来操作,这也是数组操作常用的方法。该函数可用于得到数组中任意第k大的数字(下面的两题都使用该方法)

代码实现:

  public int Partition(int[] array,int low,int high){
  	int pivot = array[low];
  	int i = low;
  	int j = high;
  	while(i<j){
  		while(i<j&&array[j]>=pivot){    //如果j指向的数大于pivot,就将j向左移动
  			j--;
  		}
  		while(i<j&&array[i]<=pivot){    //如果i指向的数小于pivot,就将i向右移动
  			i++;
  		}
  		if(i>=j){                        //当i==j时停止,写成if(i==j)也一样
  			break;
  		}
  		swap(array,i,j);                
  	}
  	swap(array,low,j);
  	return j;
  }
  public void swap(int[] array,int i,int j){
  	int temp = array[i];
  	array[i] = array[j];
  	array[j] = temp;
  }

通过上面的函数,可以达到这样的功能,数组中pivot的左边都小于它,右边都大于它,接下来,我们可以用递归的思路分别对数组左边和右边都执行Patition()函数。

实现代码:

  public void searchMid(int[] array,int low,int high){
  	if(low<high){
  		int j = Partition(array,low,high);        //array[j] = pivot
  		searchMid(array,low,j-1);
  		searchMid(array,j+1,high);
  	}
  }

2.剑指Offer题,这两题都是用复杂度为O(n)的算法得到数组中任意第k大的数字

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

题目描述

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

分析:数组中有一个数字的次数超过了数组的一半,如果把这个数组排序,那么排序之后位于数组中间数字就一定是那个出现次数超过数组长度一半的数字,即我们要找第(n/2)大的数字。可以先调用Partition函数,它返回一个j,此时如果j左边的数字都小于array[j],j右边的数字都大于array[j],如果j刚好等于n/2,那么array[j]就是第n/2个数字,如果j>n/2,那么在j的左边继续查找,如果j<n/2,那么在j的右边继续查找。

代码如下:

public int searchMid(int[] array, int low, int high) {
		int mid = array.length >> 1;                //mid = n/2
		int index = Partition(array, low, high);
		while(index!=mid){
			if(index<mid){
				low = index + 1;
				index = Partition(array,low,high);
			}
			else if(index>mid){
				high = index - 1;
				index = Partition(array,low,high);
			}
		}
		int result = array[mid];
		System.out.println("result: " + result);
		//统计一下,如果题目中数组中出现频率最高的数字次数没有达到一半,直接返回0
		int count = 0;
		for (int i = 0; i < array.length; i++) {
			if (array[i] == result)
				count++;
		}
		System.out.println("count: " + count);
		if (count > array.length / 2) {
			return result;
		} else {
			return 0;
		}
	}

说明:题中给出的数组中可能本来就没有个数超过数组一半的数,所以这里要对最后得到的数字进行判断。判断方法就是遍历该数字,求出得到数字出现的次数,看是否满足符合题意。

2)最小的k个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
分析:上面的题目是求第n/2个数,现在是求第K个数,此时该数左边的数就是最小的K个数。

代码实现:

      ArrayList<Integer> list = new ArrayList<>();
      int low = 0;
      int high = array.length - 1;
      int j = Partition(array,0,array.length-1);
      while(j!=k-1){
      	if(j>k-1){
      		high = j - 1;
      		j = Partition(array,low,high);
      	}
      	else if(j<k-1){
      		low = j + 1;
      		j = Partition(array,low,high);
      	}
      }
      for(int i=0;i<=j;i++){
      	list.add(array[i]);
      }
      return list;

上面两题的完整代码:

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

class JianZhiOffer{
	public static void main(String[] args) {
		Solution s = new Solution();
		//int[] array = {1,2,3,2,2,2,5,4,2};
		//int[] array = {1,2,3,2,4,2,5,2,3};
		int[] array = {2,2,2,2,2,1,3,4,5};
		System.out.println(s.MoreThanHalfNum_Solution(array));;
	}
}
class Solution {
  public int MoreThanHalfNum_Solution(int [] array) {
      int low = 0;
      int high = array.length - 1;
      searchMid(array, low, high);
      return searchMid(array,low,high);
  }
	public int searchMid(int[] array, int low, int high) {
		int mid = array.length >> 1;
		int index = Partition(array, low, high);
		while(index!=mid){
			if(index<mid){
				low = index + 1;
				index = Partition(array,low,high);
			}
			else if(index>mid){
				high = index - 1;
				index = Partition(array,low,high);
			}
		}
		int result = array[mid];
		System.out.println("result: " + result);
		//统计一下,如果题目中数组中出现频率最高的数字次数没有达到一半
		int count = 0;
		for (int i = 0; i < array.length; i++) {
			if (array[i] == result)
				count++;
		}
		System.out.println("count: " + count);
		if (count > array.length / 2) {
			return result;
		} else {
			return 0;
		}
	}
  
  public int Partition(int[] a,int low,int high){
      int i = low;
      int j = high;
      int temp = a[low];
      while(i<j){
          while(i<j&&a[j]>=temp){
              j--;
          }
          while(i<j&&a[i]<=temp){
              i++;
          }
          if(i>=j)
              break;
          exch(a,i,j);
      }
      exch(a,low,i);
      return i;
  }
  
  public void exch(int[] a,int i,int j){
      int temp;
      temp = a[i];
      a[i] = a[j];
      a[j] = temp;
  }
}

第二题:最小的K个数

class JianZhiOffer{
	public static void main(String[] args) {
      int[] array = {4,5,1,6,2,7,3,8};
      Solution s = new Solution();
      s.GetLeastNumbers_Solution(array, 4);
	}
}
class Solution {
  public ArrayList<Integer> GetLeastNumbers_Solution(int [] array, int k) {
      ArrayList<Integer> list = new ArrayList<>();
      int low = 0;
      int high = array.length - 1;
      int j = Partition(array,0,array.length-1);
      while(j!=k-1){
      	if(j>k-1){
      		high = j - 1;
      		j = Partition(array,low,high);
      	}
      	else if(j<k-1){
      		low = j + 1;
      		j = Partition(array,low,high);
      	}
      }
      for(int i=0;i<=j;i++){
      	list.add(array[i]);
      }
      return list;
  }
  public int Partition(int[] array,int low,int high){
  	int pivot = array[low];
  	int i = low;
  	int j = high;
  	while(i<j){
  		while(i<j&&array[j]>=pivot){
  			j--;
  		}
  		while(i<j&&array[i]<=pivot){
  			i++;
  		}
  		if(i>=j){
  			break;
  		}
  		swap(array,i,j);
  	}
  	swap(array,low,j);
  	return j;
  }
  public void swap(int[] array,int i,int j){
  	int temp = array[i];
  	array[i] = array[j];
  	array[j] = temp;
  }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值