排序练习1

来源:互联网 发布:java图形界面插件 编辑:程序博客网 时间:2024/06/05 07:46

1、小范围排序

   已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

给定一个int数组A,同时给定A的大小n和题意中的k,请返回排序后的数组。

测试样例:
[2,1,4,3,6,5,8,7,10,9],10,2
返回:[1,2,3,4,5,6,7,8,9,10]
思想:初始i=0,将A[0]~A[k-1]个数重新建立为新数组heap,进行小根堆排序,将第heap[0]个数赋值给A[i],然后将A[k](即A[k-1]的下一个数)赋值给heap[0],循环下去,直到k==n。

代码如下:

public class ScaleSort {
   public int[] sortElement(int[] A, int n, int k) {
if (A == null || n < k || A.length == 0) {
return A;
}
// 首次调整好为A[0]~A[k-1]范围的小根堆
int[] heap = firstHeap(A, k);
for (int i = k; i < n; i++) {
A[i-k] = heap[0];
heap[0] = A[i];
heapAdjust(heap, 0, k);
}
for(int j = n-k;j<n;j++){
A[j] = heap[0];
swap(heap,0,k-1);
heapAdjust(heap, 0, --k);

}
return A;
}

//循环插入A[0]~A[k-1]到heap
private int[] firstHeap(int[] A, int k) {
int[] heap = new int[k];
for (int i = 0; i < k; i ++) {
heapInsert(heap, A[i], i);
}
return heap;
}

//构建有序的heap数组
private void heapInsert(int[] heap, int value, int index) {
heap[index] = value;
while (index != 0) {
int parent = (index - 1) / 2;
if (heap[parent] > heap[index]) {
swap(heap, parent, index);
index = parent;
} else {
break;
}
}


}


private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}


/**
* 构建小根堆
*/
private void heapAdjust(int[] temp,int begin, int size) {
//左孩子
int left = begin * 2 + 1;
int right = begin * 2 + 2;
int smallest = begin;
while(left < size){

if(temp[left]<temp[smallest]){
smallest = left;
}
if(left + 1<size && temp[right]<temp[smallest]){
smallest = right;
}

if(smallest != begin){
swap(temp,begin,smallest);
}else{
break;
}
begin = smallest;
left = begin * 2 + 1;
right = begin * 2 + 2;
}
}
}


2、重复值判断

   请设计一个高效算法,判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。给定一个int数组A及它的大小n,请返回它是否有重复值。

测试样例:
[1,2,3,4,5,5,6],7
返回:true
思想:
如果没有规定空间复杂度为O(1),可以使用哈希表,但规定了空间复杂度为O(1)。所以应使用空间复杂度为O(1)的算法。此处使用堆排序。先将数组排序,这样相同的数字会靠在一起,此时再遍历数组比较前后两个数是否相同即可。
(关于算法空间复杂度相关知识可参考:http://blog.csdn.net/ack_finding/article/details/77775774)
代码如下:
public class Checker {
  public boolean checkDuplicate(int[] a, int n) {
for (int i = n / 2; i > 0; i--) {
heapAdjust(a, i - 1, n);
}
for (int j = n - 1; j > 0; j--) {
swap(a, 0, j);
heapAdjust(a, 0, j);
}
for (int k = 1; k < n; k++) {
if (a[k - 1] == a[k]) {
return true;
}
}
return false;
}

private void swap(int[] a, int index1, int index2) {
int temp = a[index1];
a[index1] = a[index2];
a[index2] = temp;
}

// 取左孩子
private int getLeftChild(int i) {
return 2 * i + 1;
}

// 调整为大根堆
private void heapAdjust(int[] a, int i, int n) {
int parent = a[i];
int leftChild = getLeftChild(i);
while (leftChild <= n - 1) {
if (leftChild < n - 1 && a[leftChild] < a[leftChild + 1]) {
leftChild++;
}
if (a[leftChild] <= parent) {
break;
}
a[(leftChild - 1) / 2] = a[leftChild];
leftChild = getLeftChild(leftChild);
}
a[(leftChild - 1) / 2] = parent;
}
}
3、有序数组合并

   有两个从小到大排序以后的数组A和B,其中A的末端有足够的缓冲空容纳B。请编写一个方法,将B合并入A并排序。给定两个有序int数组AB,A中的缓冲空用0填充,同时给定A和B的真实大小intn和intm,请返回合并后的数组。

思想:由于数组是从大到小的有序数组。从A数组实际大小n开始,与B数组从后向前比较,将大的赋值到A数组缓冲后面,该数组下标-1,A数组缓存下标-1,直到B数组完全赋值进A数组。

注意:要考虑到A数组真实大小为0的情况,此时将B全部拷贝进A即可。

代码如下:
public class Merge {
 
    public int[] mergeAB(int[] A, int[] B, int n, int m) {
    while(m!=0){
    if(n == 0){
    A[m-1]=B[m-1];
    m--;
    }else{
    A[m+n-1] = A[n-1]>B[m-1]? A[--n]:B[--m];
    }
    }
        return A;
    }
}

原创粉丝点击