排序算法
来源:互联网 发布:js 动态仪表盘 编辑:程序博客网 时间:2024/05/24 01:13
冒泡排序:o(n2)
0~n-1 两两比较,找出最大的,放在n-1位置;
0~n-2 两两比较,找出第二大的,放在n-2位置;
以此类推
class BubbleSort {
public:
int* bubbleSort(int* A, int n) {
// write code here
int temp;
int p=n;
for(int j=0;j<n-1;j++)
{
bool finish = true;//某一次没做交换,说明已整体有序
for(int i=0;i<p-1;i++)//一定要注意p的值
{
if(A[i]>A[i+1])
{
finish=false;
temp=A[i];
A[i]=A[i+1];
A[i+1]=temp;
}
}
if(finish)
break;
else
p=p-1;
}
return A;
}
};
需要注意的是,冒泡排序中需要进行n-1次比较,并且在第i次中比较到第n-i就可以了。
对于冒泡排序来说,最后面的都是排序好的!
选择排序:o(n2)
首先找到最小值,放在位置0;
再找到第二最小值,放在位置1;
以此类推
class SelectionSort {
public:
int* selectionSort(int* A, int n) {
// write code here
int min;
int temp;
for(int j=0;j<n;j++)
{
min=A[j];
for(int i=j+1;i<n;i++)
{
if(A[i]<min)
{
min=A[i];
temp=A[i];
A[i]=A[j];
A[j]=temp;
}
}
}
return A;
}
};
对于选择排序来说,前面第j个数永远都是剩下还没有排序的数中的最小的,所以剩余的数都和第j个数进行比较就可以了!!
class
SelectionSort {
public
:
int
* selectionSort(
int
* A,
int
n) {
for
(
int
i=
0
; i<n; i++){
int
location=i;
for
(
int
j=i+
1
; j<n;j++){
if
(A[location]>A[j]){
location=j;
//swap(A[j],A[j+1]);
}
}
swap(A[location],A[i]);
}
return
A;
}
};
插入排序:o(n2)
每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
class InsertionSort {
public:
int* insertionSort(int* A, int n) {
// write code here
int j,temp;
for(int i=0;i<n;i++)
{
j=i-1;
while(j>=0)
{
if(A[i]<A[j])
{
temp=A[i];
A[i]=A[j];
A[j]=temp;
i=j;
j=i-1;//这里的1就是希尔排序中的步长,插入排序的步长为1
}else{
break;
}
}
}
return A;
}
};
对于插入排序来说,最前面的都是排序好的!!
class
InsertionSort {
public
:
int
* insertionSort(
int
* A,
int
n) {
int
i,j;
for
(i =
1
; i < n; i++) {
int
cur = A[i];
for
(j = i -
1
; j >=
0
&& cur < A[j]; j--) {
A[j+
1
] = A[j];
}
A[j+
1
] = cur;
}
return
A;
}
};
上面的时间复杂度都是o(n2);
下面介绍时间复杂度为o(n*logn)的算法:
归并排序;
快速排序;
堆排序;
希尔排序;
归并排序:多个长度为1的有序区间;
多个长度为1的相邻有序区间经过两两合并得到多个长度为2的有序区间;
以此类推;
主要用到递归的思想,合并操作。
class MergeSort {
public:
int* mergeSort(int* A, int n) {
// write code here
int *p;
int h,count,len,f;
count=0;
len=1;
f=0;
if(!(p=(int*)malloc(sizeof(int)*n)))//这里一定要用malloc分配内存,最后才能用free释放。并且不能将p的申请内存的函数放到MergeOne里面,否则要不断的为p分配内存
//容易引发错误
{
// printf("内存分配失败");
exit(0);
}
while(len<n)
{
if(f==1)
{
MergeOne(p,A,n,len);
}
else{
MergeOne(A,p,n,len);
}
len=len*2;
f=1-f;
count++;
}
//printf("第%d步排序结果是:",count);
/*for(h=0;h<n;h++)
{
printf("%d",A[h]);
}
printf("\n");
}*/
if(f)
{
for(h=0;h<n;h++)
A[h]=p[h];
}
free(p);
return A;
}
void MergeOne(int* A,int*b,int n,int len)
{
int i,j,k,s,e;
s=0;
while(s+len<n)
{
e=s+2*len-1;
if(e>=n)
{
e=n-1;
}
k=s;
i=s;
j=s+len;
while(i<s+len&&j<=e)
{
if(A[i]<A[j])
{
b[k++]=A[i++];
}else{
b[k++]=A[j++];
}
}
while(i<s+len)
{
b[k++]=A[i++];
}
while(j<=e)
{
b[k++]=A[j++];
}
s=e+1;
}
if(s<n)
{
for(;s<n;s++)
b[s]=A[s];
}
}
};
上面是归并排序的程序,这个程序首先要判断len的长度是否小于n,如果len长度正好为n或者大于n,那么排序就已经结束了;
然后在进行排序的时候,也就是在MergeOne函数中,s作为起始序列的位置,首先要判断起始序列后面的序列是否还有内容,如果
s+len等于n或者大于n,就代表只有起始序列有内容,后面第二个序列没有内容,所以不需要进行比较,直接将起始序列合并到已经
排好序的数组中就可以了,也就是下面的if(s<n)这一段;但是如果起始序列后面的序列还有内容,就需要进行比较判断了。当然首先
要计算中第二个序列的起始位置和结束位置,分别为s+len, s+2*len-1;当然如果第二个序列的长度不足len的话,也就是最后剩下的不足
len长度的序列,当然结束位置就是整个数组结束的位置,就是n-1;最后进行正常的比较合并就可以了。
class MergeSort {
public:
int* mergeSort(int* A, int n) {
// write code here
int p[n];
mergeOne(A,p,0,n-1);
return A;//这里是重点!!!
}
void mergeOne(int* A,int* p,int start, int end){
if(start>=end){
return;
}//这里非常重要,否则会一直递归下去
int middle=(start+end)/2;
mergeOne(A,p,start,middle);
mergeOne(A,p,middle+1,end);
// merging(A,p,start,middle,end);
//}
// void merging(int* A,int* p,int start,int middle,int end){
int a=start;
int b=middle+1;
int k=start;
while(a<=middle&&b<=end){
if(A[a]<A[b])
p[k++]=A[a++];
else
p[k++]=A[b++];
}
while(a<=middle)
{
p[k++]=A[a++];
}
while(b<=end)
{
p[k++]=A[b++];
}
for(int i=start;i<=end;i++)//这里是重点
{
A[i]=p[i];
}
//memcpy(A+start, p+start, (end - start + 1) * (sizeof(int) / sizeof(char)));
}
};
上面的程序是利用递归思想进行的归并排序!!需要注意的是,最后将p的值赋值给A的时候,也可以借助于memcpy函数,效果都是一样的!!同时将比较合并的步骤单独写成一个函数void merging结果也是正确的!并且,对于数组p,用int* p=new int[n];进行定义也是可以的,只不过需要在return A的后面加上一句delete p;。
快速排序:也用到递归的思想
划分过程:主要思想是确定枢轴量之后,确定小于这个枢轴量的数的区间以及大于这个枢轴量的数的区间,然后再递归调用快速排序算法:
class QuickSort {
public:
int* quickSort(int* A, int n) {
quickSort(A, 0, n-1);
return A;
}
private:
void quickSort(int* A, int low, int high) {
if (low < high) {
int pivot = partition(A, low, high);
quickSort(A, low, pivot-1);
quickSort(A, pivot+1, high);
}
}
int partition(int* A, int low, int high) {
int pivotkey = A[high];
while (low < high) {
while (low < high && pivotkey > A[low])
low++;
swap(A[low], A[high]);
while (low < high && A[high] >= pivotkey)
high--;
swap(A[low], A[high]);
}
return high;
}
};
快速排序算法就是挖坑填数+分治法:
以一个数组作为示例,取区间第一个数为基准数。
0
1
2
3
4
5
6
7
8
9
72
6
57
88
60
42
83
73
48
85
初始时,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--;以此类推,上面的例子是摘抄自http://blog.csdn.net/morewindows/article/details/6684558。我认为挖坑填数这个形容非常贴切,对于上面的程序来说,一开始是将high作为枢轴量,如果想将low作为枢轴量,想一下挖坑填数,就是说,一开始在low的地方挖了一个坑,下一步就是从high向前数找到一个小于枢轴量的数来将low的地方的坑给填充上,这时候high地方出现了一个新的坑,以此类推,程序如下:
class QuickSort {
public:
int* quickSort(int* A, int n) {
quickSort(A, 0, n-1);
return A;
}
private:
void quickSort(int* A, int low, int high) {
if (low < high) {
int pivot = partition(A, low, high);
quickSort(A, low, pivot-1);
quickSort(A, pivot+1, high);
}
}
int partition(int* A, int low, int high) {
int pivotkey = A[low];
while (low < high) {
while (low < high && pivotkey<A[high])
high--;
swap(A[low], A[high]);
while (low < high && A[low] <= pivotkey)
low++;
swap(A[low], A[high]);
}
return low;
}
};
当然,还可以将中间的数作为枢轴量,要实现这个方便非常方便,直接将中间的数和第一个数进行交换就可以了。程序如下:
class QuickSort {
public:
int* quickSort(int* A, int n) {
quickSort(A, 0, n-1);
return A;
}
private:
void quickSort(int* A, int low, int high) {
if (low < high) {
int pivot = partition(A, low, high);
quickSort(A, low, pivot-1);
quickSort(A, pivot+1, high);
}
}
int partition(int* A, int low, int high) {
int temp= A[(low+high)/2];
A[(low+high)/2]=A[low];
A[low]=temp;//将第一个数和中间数进行交换
int pivotkey =A[low];
while (low < high) {
while (low < high && pivotkey<A[high])
high--;
swap(A[low], A[high]);
while (low < high && A[low] <= pivotkey)
low++;
swap(A[low], A[high]);
}
return low;
}
};
堆排序:数组中的n个数建立成一个大小为n的最大堆,堆顶元素是所有元素中的最大值,将堆顶元素和堆的最后一个元素进行交换,
将堆顶元素脱离这个堆结构,放到数组的最后的位置;然后对剩余的n-1的堆从堆顶进行大根堆的调整,再找出这些值中的最大值于堆顶位置,然后和堆最后的元素进行交换,.......,当堆大小变成1,排序结束。
堆的存储:一般用数组来表示堆,i节点的父节点下标就是(i-1)/2,因此它的左右节点下标分别为2*i+1和2*i+2。
堆的插入:向堆中插入数据时 是将数据插入到数组最后,调整的时候是自下向上调整;
堆的删除:删除堆中的数据每次都只能删除数组中第0个数据,即将最后一个数据的值赋给根节点,然后再从根节点开始进行一次从上向下的调整,调整时先在左右儿子中找最小的,如果父节点比这个最小的子节点还小说明不需要调整了;反之将父节点个和它交换后再考虑后面的节点;
堆化数组:就是从上向下调整堆的过程,那么从哪个节点开始调整呢?一定是从后面开始数第一个有子节点的节点开始的,这个节点就是最后一个节点也就是第n-1个节点的父节点!!!根据前面的公式第n-1个节点的父节点为(n-1-1)/2也就是n/2-1。需要注意的是:注意一定是从上向下进行彻底的调整,不能只调整当前节点和它的左右子节点,而应该调整当前节点和它的左右子节点之后,如果真的发生的交换,那么一定要继续向下调整!!否则很有可能导致错误!!
堆排序:由上面的知识可知,堆排序的过程就是先堆化数组,然后取出第0个数据,然后删除第0个元素,调整堆,重复上述步骤,直至堆中只有一个数据时就直接取出这个数据。
class HeapSort {
public:
int* heapSort(int* A, int n) {
// write code here
int i=(n-1-1)/2;
//下面是调整数组成为最大堆数组的函数
for(;i>=0;i--)
MaxHeapFixdown( A, i,n);//数组大小为n,从第i个数组开始调整
//下面是将堆中的数据从第0个数据开始输出,然后删除调整的函数,也就是说将第0个数据和最后一个数据进行交换,然后对堆进行从上到下的调整
for(int j=n-1;j>=1;j--){
int temp=A[0];
A[0]=A[j];
A[j]=temp;
//swap(A[0],A[j]);
MaxHeapFixdown(A,0,j);//将第一个数据和最后一个数据交换之后,最后的数据就是最大的数据,不需要参与到堆的调整了
}
return A;
}
void MaxHeapFixdown( int* A, int i,int n)
{
int max;
int current=A[i];
while(2*i+1<n){
max=2*i+1;
if(max+1<n&&A[max]<A[max+1])
max=max+1;
if(current<A[max]){//这里是current和当前最大值进行比较!!!!一定要记住!!!
A[i]=A[max];
i=max;
}else{
break;//注意这个位置一定要有break
}
}
A[i]=current;
}
};
上面的堆排序的程序,这个程序主要分两个步骤,首先是堆用数组表示,当然这是一定给定的就是一个数组;然后将堆调整成最大堆,也就是函数MaxHeapFixdown,这里的调整时从上向下调整的,这个函数非常非常重要!!!而调整的顺序是从倒数第一个有孩子节点的节点开始的!!最大堆调整结束之后,就使排序了!每次将当前最大值也就是第0个元素和最后一个元素交换,这样最大值就被放在数组后面的位置了!!
补充一点,因为向堆中插入数据都是插入到最后面的,所以之后对堆的调整应该是从下到上的调整;而删除堆的数据通常是删除第一个元素,然后将最后的元素放到第0个元素的位置,所以是从上向下调整;而堆化数组也是从上下向下调整的,只不过是从倒数第一个有子节点的节点开始的!!
希尔排序:比较特殊!!插入排序改良的算法,步长经过了调整,插入排序步长为1,希尔排序步长是动态从大到小调整的!!之所以说插入排序的步长为1,是因为他每次都是和前一个值进行比较,如果小则进行交换,然后再和前一个值进行比较,直到不能交换停止;而希尔排序中,每次都是和第前n个值进行比较,n就是步长!!!n步长结束之后,会进行下一轮的步长为n-1的调整;以此类推,直到最后步长为1,也就是插入排序的调整!关键在于步长的选择!!!
class ShellSort {
public:
int* shellSort(int* A, int n) {
// write code here
int shell=n/2;
while(shell>=1){
shellSort1(A,shell,n);
shell=shell/2;
}
return A;
}
void shellSort1(int* A,int shell,int n){
for(int i=0;i<n;i++){
int j=i-shell;//shell为增量,直接插入排序的时候为1
int temp=i;
int current=A[i];
while(j>=0){
if(current<A[j]){
A[temp]=A[j];
j=j-shell;
temp=temp-shell;
}else{
break;
}
}
A[temp]=current;
}
}
};
对于上面的程序来说,其实shellSort1函数中,shell为1的时候就是插入排序。
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- 排序算法
- PAT 1006. 换个格式输出整数 (15) —— Java
- iptables不完全手册
- 拥塞控制之慢开始和拥塞避免
- Java-按指定顺序执行线程
- Leetcode 239 Sliding Window Maximum
- 排序算法
- 【MongoDB】mongoimport mongoexport
- Powershell调用Excel批量读取指定单元格数据并输出csv结果文件
- 【MongoDB】mongo复制数据库和集合
- win10桌面图标和菜单栏不时的闪烁问题
- ubuntu14.04安装node+grunt
- Activity中的数据传递
- java字符串大小写转化
- 康托展开