快速排序,归并排序

来源:互联网 发布:星星网络在线星盘 编辑:程序博客网 时间:2024/05/21 07:11

#include<stdio.h>

void _partition(int arr[],int l,int mid,int r){ //归并的核心代码

int aux[r - l + 1];//辅助的数组,演示的下面的示例

//aux的赋值,aux是冲0开始的,但是arr不是从0开始的,因为递归传上来的数,不已定是从0开始的,先左到n,在左右到n递归,所以不一定l就是为0

for(int i = l ; i <= r ; i++)

aux[i - l] = arr[i];

int i = l,j = mid + 1;//演示中i,j指向的位置,karr后赋值过来归并好的数组

for(int k = l ; k <= r ; k++){ //kL开始,不然的话会数组重叠

if(i > mid){

arr[k] = aux[j - l];

j++;

}

else if(j > r){

arr[k] = aux[i - l];

i++;

}

else if(aux[i - l] < aux[j - l]){

arr[k] = aux[i - l];

i++;

}

else{

arr[k] = aux[j - l];

j++;

}

}

}

void _mergeSort(int arr[],int l,int r){ //进行递归函数的编写,分和归

//递归的编写必须要有退出的条件

if(l >= r){

return ;

}

int mid = (l + r) / 2;

_mergeSort(arr,l,mid); //可以优化

_mergeSort(arr,mid + 1,r);

if(arr[mid] > arr[mid + 1]) //当时逆序的时候 才进行归并的排序

_partition(arr,l,mid,r);

}

void mergeSort(int arr[],int n){

_mergeSort(arr,0,n - 1); // 闭区间

}

int main(){

int arr[7] = {5,9,10,3,2,1,4};

mergeSort(arr,7);

for(int i = 0 ; i < 7 ; i++)

printf("%d ",arr[i]);

return 0;

}

 

另一个(ologn)的排序算法是快速排序(QuickSort),它与归并排序一样,都是采用了相同的思想(分治的思想)

该算法的主要的思想是:

1:从数列中取出一个数作为基数,当然这个数可以为任何数,在QuickSort的优化中,通常我们会使用random取数列中的随机数作为基数

2:分区过程,将比这个基数大的放在右边,比这个数的基数小的数放在左边,其中会出现一个等于该基数的数,此时如果该数多的话,我们可以使用3路快速排序,从而减少时间的损耗,因为快速排序是一个不稳定的排序方法,他的期望是(nlogn)的时间复杂度,所以应该避免它的分层不平衡从而增加他的时间复杂度

3:运用递归的方法,再对左右区间第二部,知道区间只有一个数

 

挖坑填数+分治法

0

1

2

3

4

5

6

7

8

9

72

6

57

88

60

42

83

73

48

85

初始的时候,i等于0j等于9X等于a[i] = 72;

初始时,i = 0;  j = 9;   X = a[i] = 72

由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。

j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;

 

数组变为:

0

1

2

3

4

5

6

7

8

9

48

6

57

88

60

42

83

73

88

85

 i = 3;   j = 7;   X=72

再重复上面的步骤,先从后向前找,再从前向后找

j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

i开始向后找,当i=5时,由于i==j退出。

此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]

 

数组变为:

0

1

2

3

4

5

6

7

8

9

48

6

57

42

60

72

83

73

88

85

可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]a[6…9]这二个子区间重复上述步骤就可以了。

对该挖坑填数的总结:

1i = L,j = R,将基准数挖出形成第一个坑ai;

2: j--由后向前找比它小的数,找到后挖出此数前的一个坑ai,特别注意:该基数不会因为变化位置导致基数的变化

3i++由前到后找比它大的数,找到后也挖出此数填充到前一个坑中ai】中

4:递归要有退出的条件,再重复2,3的步骤,知道i == j,将基数填入ai】中

核心代码

#include<stdio.h>

void _quickSort(int arr[],int l,int r){

if(l < r){ //进入该函数的条件

int i = l,j = r,x = arr[l];

while(i < j){ //退出的条件

while(i < j && arr[j] >= x) //作用是偏移,移动到可以交换的位置,在下面进行交换

j--;

if(i < j){

arr[i] = arr[j];

i++;

}

while(i < j && arr[i] < x)

i++;

if(i < j){

arr[j] = arr[i];

j--;

}

}

arr[i] = x;

_quickSort(arr,l,i - 1);

_quickSort(arr,i + 1,r);

}

}

void quickSort(int arr[],int n){

_quickSort(arr,0,n - 1);

}

int main(){

int arr[7] = {5,2,3,9,7,6,1};

quickSort(arr,7);

for(int i = 0 ; i < 7 ; i++)

printf("%d ",arr[i]);

return 0;

}

以上为2路快速排序

当然还有很多可以减少时间复杂度的方法,例如3路的快排,以及用随机的方法进行选取基数,因为快速排序是一个不稳定的(nlogn)的算法排序,目的是为了让两边更加平衡,不使其变得不那么混乱,时间复杂度范围最大来(n*n;

3路的快速排序主要是为了解决存在多个等于该基数的数,减少2路快排的所需的时间,减少时间复杂度

#include<stdio.h>

void swap(int a,int b){

int temp = a;

a = b;

b = temp;

}

void _quickSort3way(int arr[],int l,int r){

if(l < r){

int v = arr[l];

int lt = l; //arr[l + 1.....lt] < v

int gt = r + 1;//arr[gt.....r] > v

int i = l + 1;//arr[lt + 1.....i] == v

while(i < gt){ //退出的条件

if(arr[i] < v){

swap(arr[i],arr[lt + 1]);

lt++;

i++;

}

else if(arr[i] > v){

swap(arr[i],arr[gt - 1]);

gt--;

}

else{

i++;

}

}

swap(arr[l],arr[lt]);

_quickSort3way(arr,l,lt - 1);

_quickSort3way(arr,gt,r);

}

}

void quickSort3way(int arr[],int n){

_quickSort3way(arr,0,n - 1);

}

int main(){

int arr[7] = {5,2,3,9,7,6,1};

quickSort3way(arr,7);

for(int i = 0 ; i < 7 ; i++)

printf("%d ",arr[i]);

return 0;

}

原创粉丝点击