一.快速排序
首先介绍一下快速排序,因为这两题都可以使用快速排序的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)数组中出现次数超过一半的数字
题目描述
分析:数组中有一个数字的次数超过了数组的一半,如果把这个数组排序,那么排序之后位于数组中间数字就一定是那个出现次数超过数组长度一半的数字,即我们要找第(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个数
题目描述
代码实现:
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;
}
}