题目链接:
题目:
解题思路:
(1)利用快排对输入数字进行排序,然后取最小的k个数。
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
if len(tinput) < k or len(tinput) == 0:
return []
list = self.Sort(tinput, 0, len(tinput) - 1)
return list[0:k]
def Sort(self, array, low, high):
if low < high:
index = self.partition(array, low, high)
self.Sort(array, low, index-1)
self.Sort(array, index+1, high)
return array
def partition(self, array, low, high):
key = array[low]
while low < high:
while low < high and array[high] > key:
high = high - 1
array[low] = array[high]
while low < high and array[low] < key:
low = low + 1
array[high] = array[low]
array[high] = key
return high
if __name__ == '__main__':
list = [4, 5, 1, 6, 2, 7, 3, 8]
solution = Solution()
print solution.GetLeastNumbers_Solution(list, 4)
代码是写出来了,但是没有通过。
天哪!都没通过,感觉这样写是拿不到offer的。
有一个问题,同样是一个思路,用Python不能通过,但是用Java就能通过。
通过:
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> arr=new ArrayList<>();
if(input.length<k||input.length==0) return arr;
sort(input,0,input.length-1);
for(int i=0; i<k; i++)
arr.add(input[i]);
return arr;
}
public void sort(int[] a, int left, int right){
if(left<right){
int mid=isMid(a,left,right);
sort(a,left,mid-1);
sort(a,mid+1,right);
}
}
public int isMid(int[] a,int left, int right){
int mid=a[left];
while(left<right){
while(a[right]>=mid && left<right)
right--;
a[left]=a[right];
while(a[left]<=mid && left<right)
left++;
a[right]=a[left];
}
a[left]=mid;
return left;
}
}
Python快排聪明人的写法,通过:
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
def quick_sort(lst):
if not lst:
return []
pivot = lst[0]
left = quick_sort([x for x in lst[1:] if x < pivot])
right = quick_sort([x for x in lst[1:] if x >= pivot])
return left + [pivot] + right
if tinput == [] or k > len(tinput):
return []
tinput = quick_sort(tinput)
return tinput[: k]
if __name__ == '__main__':
list = [4, 5, 1, 6, 2, 7, 3, 8]
solution = Solution()
print solution.GetLeastNumbers_Solution(list, 4)
(2)看到牛客网上有人根据快排的思想,改进了一下算法。
利用快速排序中的获取分割(中轴)点位置函数partition。
基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。调整之后,位于数组左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。O(N)
用Python写不通过,但是用C++实现就通过了!对Python也是无语了。
Python不通过:
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
def partition(array, low, high):
key = array[low]
while low < high:
while low < high and array[high] > key:
high = high - 1
array[low] = array[high]
while low < high and array[low] < key:
low = low + 1
array[high] = array[low]
array[high] = key
return high
if len(tinput) == 0 or k > len(tinput) or k<=0:
return []
index = partition(tinput, 0, len(tinput)-1)
while index != (k-1):
if index > (k-1):
index = partition(tinput, 0, index-1)
else:
index = partition(tinput, index+1, len(tinput)-1)
return tinput[0:k]
if __name__ == '__main__':
list = [4, 5, 1, 6, 2, 7, 3, 8]
solution = Solution()
print solution.GetLeastNumbers_Solution(list, 8)
Python写这道题总是超时。
C++通过代码:
class Solution {
public:
void swap(int &fir,int &sec)
{
int temp = fir;
fir = sec;
sec = temp;
}
int getPartition(vector<int> &input,int start,int end)
{
if(input.empty() || start>end) return -1;
int temp = input[end];
int j = start - 1;
for(int i=start;i<end;++i)
{
if(input[i]<=temp)
{
++j;
if(i!=j) swap(input[i],input[j]);
}
}
swap(input[j+1],input[end]);
return (j+1);
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
vector<int> result;
if(input.empty() || k>input.size() || k<=0) return result;
int start = 0;
int end = input.size()-1;
int index = getPartition(input,start,end);
while(index != (k-1))
{
if(index > (k-1))
{
end = index - 1;
index = getPartition(input,start,end);
}
else
{
start = index + 1;
index = getPartition(input,start,end);
}
}
for(int i=0;i<k;++i)
{
result.push_back(input[i]);
}
return result;
}
};
总结:这种思路虽时间复杂度不错,但会修改输入数组,且一般也不易想到。更容易想到的是利用堆排序。
(3)堆排序,O(N logK),适合处理海量数据。
用最大堆保存这k个数,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆。
1) 遍历输入数组,将前k个数插入到推中;
2) 继续从输入数组中读入元素做为待插入整数,并将它与堆中最大值比较:如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,则抛弃这个数,继续读下一个数。
这样动态维护堆中这k个数,以保证它只储存输入数组中的前k个最小的数,最后输出堆即可。
Python通过代码:
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
def siftup(lst, temp, begin, end):
if lst == []:
return []
i, j = begin, begin * 2 + 1
while j < end:
if j + 1 < end and lst[j + 1] > lst[j]:
j += 1
elif temp > lst[j]:
break
else:
lst[i] = lst[j]
i, j = j, 2 * j + 1
lst[i] = temp
def heap_sort(lst):
if lst == []:
return []
end = len(lst)
for i in range((end // 2) - 1, -1, -1):
siftup(lst, lst[i], i, end)
for i in range(end - 1, 0, -1):
temp = lst[i]
lst[i] = lst[0]
siftup(lst, temp, 0, i)
return lst
if tinput == [] or k > len(tinput):
return []
tinput = heap_sort(tinput)
return tinput[: k]
Java通过代码:
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list=new ArrayList<Integer>();
//检查输入的特殊情况
if(input==null || input.length<=0 || input.length<k){
return list;
}
//构建最大堆
for(int len=k/2-1; len>=0; len--){
adjustMaxHeapSort(input,len,k-1);
}
//从第k个元素开始分别与最大堆的最大值做比较,如果比最大值小,则替换并调整堆。
//最终堆里的就是最小的K个数。
int tmp;
for(int i=k; i<input.length; i++){
if(input[i]<input[0]){
tmp=input[0];
input[0]=input[i];
input[i]=tmp;
adjustMaxHeapSort(input,0,k-1);
}
}
for(int j=0; j<k; j++){
list.add(input[j]);
}
return list;
}
public void adjustMaxHeapSort(int[] input, int pos, int length){
int temp;
int child;
for(temp=input[pos]; 2*pos+1<=length; pos=child){
child=2*pos+1;
if(child<length && input[child]<input[child+1]){
child++;
}
if(input[child]>temp){
input[pos]=input[child];
}else{
break;
}
}
input[pos]=temp;
}
}
总结:
1.基于堆排序算法,构建最大堆的时间复杂度为 O(nlogk)。
2.如果用快速排序,时间复杂度为 O(nlogn)。
3.如果用冒泡排序,时间复杂度为 O(n*k)。