一、拓扑排序
拓扑排序的步骤:
(1)从图中选择一个入度为0的顶点且输出之;
(2)从图中删掉该顶点及其所有以该顶点为弧尾的弧;
反复执行这两个步骤,直到所有的顶点都被输出,输出的序列就是这个无环有向图的拓扑序列。
二、递归归并排序、二路归并排序
归并算法的两种方法:
1、使用分治法的递归归并算法:
/*递归归并排序
*将有二个有序数列list[first...mid]和list[mid+1,...last]合并
*list:待排序数组
*first:子序列1的下界
*mid:子序列1的上界
*last:子序列2的上界
*temp:临时保存数组
*/
void Merge(element list[], int first, int mid, int last, element temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (list[i] < list[j])
temp[k++] = list[i++];
else
temp[k++] = list[j++];
}
while (i <= m)
temp[k++] = list[i++];
while (j <= n)
temp[k++] = list[j++];
for (i = 0; i < k; i++)
list[first + i] = temp[i];
return ;
}
/*递归归并排序
*分治,完成递归归并
*list:待排序数组
*first:list下界
*last:list上界
*temp:临时保存数组
*/
void mergesort(element list[], int first, int last, element temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(list, first, mid, temp); //左边有序
mergesort(list, mid + 1, last, temp); //右边有序
Merge(list, first, mid, last, temp); //再将二个有序数列合并
}
return ;
}
/*递归归并排序
*list:待排序数组
*n:为数组长度
*/
bool MergeSort(element list[], int n)
{
element *p = new element[n];//空间复杂度为O(n)
if (p == NULL)
exit(1);
mergesort(list, 0, n - 1, p);
delete[] p;
return true;
}
2、二路归并算法:
/*归并算法二,二路归并
*list:待排序数组
*n:为数组长度
*temp:临时保存数组
*k:为有序子数组的长度,一次二路归并排序后有序子序列存于temp数组中
*/
void Merge2(element list[], int n, element temp[], int k)
{
int i, j;
int m=0;
int low1=0;//第一有序的下界
int up1, low2, up2;//第一有序的上界,第二有序的下界,第二有序的上界
/*将原始数组中足够分为两组的数据元素二路归并存放到数组temp中*/
while(low1+k < n-1)
{
low2 = low1+k;
up1 = low2-1;
up2 =(low2+k-1 < n-1) ? low2+k-1:n-1;
for(i=low1, j=low2; i<=up1 && j <=up2; )
{
if (list[i] <= list[j])
temp[m++] = list[i++];
else
temp[m++] = list[j++];
}
while (i <= up1)
temp[m++] = list[i++];
while (j <= up2)
temp[m++] = list[j++];
low1 = up2+1;
}
/*将原始数组中只够一组的数据元素顺序存放到数组temp中*/
i=low1;
while(i<n)
{
temp[m++] = list[i++];
}
return;
}
/*二路归并排序
*list:待排序数组
*n:为数组长度
*/
bool MergeSort2(element list[], int n)
{
element *p = new element[n];//空间复杂度为O(n)
if (p == NULL)
exit(1);
int k=1;//二路归并长度从1开始
int i=0;
while(k<n)
{
Merge2(list, n, p, k);//每次归并后保存在p中
for(i=0; i<n; i++)
list[i] = p[i];//从p中拷贝到list中
k *=2;//归并长度加倍
}
delete[] p;
return true;
}
三、希尔排序
/*@希尔排序:分组插入排序
*@list:待排序数组
*@n:总排序元素个数
*/
void shellSort(element list[], int n)//希尔排序
{
int span=n;
element temp;
while(span > 1)//跨度为1时排序结束
{
span=(span+1)/2;
for(int i=0; i<n-span; i++)
{
if(list[i+span]<list[i])
{
temp=list[i+span];
list[i+span]=list[i];
list[i]=temp;
}
}
}
}
四、插入排序、选择排序 、冒泡排序
/*
*@list:待排序数组
*@n:总排序元素个数
*/
void insertSort(element list[], int n)//插入排序
{
int i=0;
int j=0;
element next;
for(i=0; i<n; i++)
{
next=list[i];
for(j=i-1;j>=0&&list[j]>next;j--)//j>=0且list[j]是和next比较
{
list[j+1]=list[j];//后一位等于前一位
}
list[j+1]=next;
}
return;
}
/*
*@list:待排序数组
*@n:总排序元素个数
*/
void selectSort(element list[], int n)//选择排序
{
int i=0;
int j=0;
int k=0;
element temp;
for(i=0; i<n; i++)
{
temp=list[i];
k=i;//不要忘记
for(j=i+1; j<n; j++)
{
if(list[j]<temp)
{
temp=list[j];
k=j;
}
}
list[k]=list[i];
list[i]=temp;
}
return;
}
/*
*@list:待排序数组
*@n:总排序元素个数
*/
void bubbleSort(element list[], int n)//冒泡排序
{
int i=0;
int j=0;
element temp;
bool exchange;//标记本趟是否有交换
for(i=0; i<n; i++)
{
exchange=false;//本趟排序开始
for(j=0; j<n-i-1; j++)//注意j的范围
{
if(list[j]>list[j+1])//j+1不能是后面排序好的元素,所以j<n-i-1
{
temp=list[j];
list[j]=list[j+1];
list[j+1]=temp;
exchange=true;//本趟排序发生交换
}
if(!exchange)//本趟排序没有发生交换,说明前n-1个不用排序,提前终止算法
{
return;
}
}
}
return;
}
五、堆排序
/*
*@list:待排序数组
*@root:根结点下标
*@n:总排序元素个数
*/
void adjust(element list[], int root, int n)//调整最大堆方法一
{
int child=2*root+1;//左孩子
element temp;
while(child < n)//***while***
{
if(child < n-1 && list[child]<list[child+1])//比较左右结点,得到较大的为child
{
//注意child < n-1
child++;
}
if(list[root] > list[child])//比较父节点与较大子节点child
{
break;
}
else{
/*交换父子节点*/
temp = list[root];
list[root]=list[child];
list[child]=temp;
/*继续向下,特别要注意这里的*/
root=child;
child = 2*child +1;
}
}
}
void adjust2(element list[], int root, int n)//调整最大堆方法二
{
int child=2*root+1;//左孩子
element rootkey=list[root];//标记最初根节点结点
while(child < n)//***while***
{
if(child < n-1 && list[child]<list[child+1])
{
child++;//比较左右结点,得到较大的为child
}
if(rootkey > list[child])//比较最初根节点结点 与 此时的最大子结点
{
break;
}
else{
//这里不是交换父子结点,和insertSort思想类似
list[root]=list[child];//父节点等于较大孩子结点
//分别向下移动
root=child;
child = 2*child +1;
}
}
list[root]=rootkey;//找到最初根结点的合适位置
}
/*
*@list:待排序数组
*@n:总排序元素个数
*/
void heapsort(element list[],int n)//堆排序
{
int i;
element temp;
/*初始化得到最大堆*/
for(i=n/2;i>=0;i--) //从n/2处依次向前调整
adjust(list,i,n); //或者采用 adjust2
for(i=n-1;i>0;i--)//i为剩余规模
{
temp=list[i]; //交换根结点和i结点
list[i]=list[0];
list[0]=temp;
//或者采用 adjust2
adjust(list,0,i);//重新调整,此时参数root为0,规模n为i
}
}
六、快速排序(最后修改)
//2012-07-16
void quickSort(element list[], int left, int right)//快速排序
{
int i=left;
int j=right;
if(i >= j) //判断需要i<j
return;
element temp=list[i];
while(i<j)
{
while(i<j && list[j]>temp)//需要i<j
j--;
list[i]=list[j];
i++;
while(i<j && list[i]<temp)//需要i<j
i++;
list[j]=list[i];
j--;
}
list[j+1]=temp;// 运行到此处j j+1 i
quickSort(list,left,j);
quickSort(list,i,right);
}
void quickSort2(element list[], int left, int right)
{
int i=left;
int j=right;
if(i>=j)
return;
element pivot=list[i];
element temp;
j++;//以下i++,j--了一次,i是需要首先+1,但j不需要,所以此处需要提前+1,抵消
do{
do{
i++;
}while( list[i]<pivot);//不需要i<j,便于之后的交换操作和子快速排序
do{
j--;
}while(list[j]>pivot);//不需要i<j
if(i<j)
{
temp=list[i];
list[i]=list[j];
list[j]=temp;
}
}while(i<j);//运行到此处,a[i]>pivot,a[j]<pivot,j=i-1;
//一般情况结束时left...j,i...right [j]<pivot,[i]>pivot所以交换left和j,
//特殊情况结束时left......right,i=j=right+1//1,2,3,4,5,6,7
//即结束时,i后的都大于pivot,j前的都小于pivot,其中[j]<pivot,[i]>pivot
list[left]=list[j];
list[j]=pivot;
quickSort(list,left,j-1);
quickSort(list,j+1,right);
}
下面的是对quickSort方法的修改:
删除了quickSort的18、24行,以及27、29、30进行对应修改。修改之后使得如下代码运行到23行时,i=j,这样思维更清晰。
★★★★★void quickSort3(element list[], int left, int right)//快速排序,修改时间2012-09-18 16:40:40
{
int i=left;
int j=right;
if(i >= j) //判断需要i<j
return;
element temp=list[i];
while(i<j)
{
while(i<j && list[j]>temp)//需要i<j
j--;
list[i]=list[j];
while(i<j && list[i]<temp)//需要i<j
i++;
list[j]=list[i];
}
list[i]=temp;// 运行到此处i=j
quickSort3(list,left,i-1);
quickSort3(list,j+1,right);
}
七、计数排序
计数排序是一种运行时间在输入的某种假设情况下可以为Θ(n)的算法,它的过程中没有比较环节。
基本的思路就是假设输入序列中任意的元素x都满足x∈[0, k],且x和k都为整数。然后对每一元素x,都确定出序列中比它小的元素的个数,比如为n,则x排序后的位置就应当从n + 1处开始。实现的时候还需要考虑一些细节,比如序列中有几个元素大小相等,因此还需要对大小相等的元素个数进行计数,这样才能正确分配排序后各个元素的位置。
过程中用到了一个辅助序列C,C的大小为k + 1,从C[0]到C[k],它的索引i代表序列中可能出现的大小为i的数,C[i]表示这个数有多少个。下面是算法导论上的例子:
待排序的序列:A = [2, 5, 3, 0, 2, 3, 0, 3] 计数序列:C = [2, 0, 2, 3, 0, 1]
索引: 0 1 2 3 4 5
表示序列中0, 1, 2, 3, 4, 5的个数分别为2, 0, 2, 3, 0, 1。
C序列中此时已经隐含了原序列中各个元素应该存放的位置,比如C[0] = 2,意味着大小为0的元素应当占据位置1, 2,而大小为1的元素应当从3开始,但原序列中没有1,因此向后遍历,大小为2的元素从3开始,个数为2,因此占据位置3, 4,以此类推。C[i]从0到n - 1求和的结果就是原序列中比n小的元素个数,假设为m,因此n应当从m开始,一直到m + c[n] - 1。
代码:(2012年7月16日 10:22:13)
/*
*@list:待排序数组,要求list[i]是大于等于0的整数;
*@n:总排序元素个数
*/
void countSort(element list[],int n)//计数排序
{
int max=list[0];
for(int i=1; i<n; i++)//计算数组中最大元素
{
if(list[i]>max)
max=list[i];
}
element* count;
count =(element*)malloc(sizeof(element)*(max+1));//数组下标为[0,...max]
if(count==NULL)
exit(1);//申请空间失败
for(int i=0; i<=max; i++)//初试化计数数组
{
count[i]=0;
}
for(int i=0; i<n; i++)//计数
{
int index=list[i];
count[index]++;
}
int j=0;
for(int i=0; i<=max; i++)//根据计算数组排序
{
while(count[i]&&j<n)
{
list[j]=i;
count[i]--;
j++;
}
}
free(count);
count=NULL;
}
八、二分查找
//非递归二分查找算法
/*list:待查找的有序数组
*key:需要查找的元素
*n:数组长度
*return:返回查找的位置下标,返回-1表示未找到
*/
int binarySearch(element list[], element key, int n)
{
int left =0;
int right = n-1;
int mid=0;
while(left <= right)// <=,不能遗漏等于的情况
{
mid=left+(right-left)/2;
if(list[mid] == key)
return mid;
else if(list[mid] < key)
left=mid+1;
else
right=mid-1;
}
return -1;
}
//递归二分查找算法
/*list:待查找的有序数组
*key:需要查找的元素
*left:数组下界
*right:数组上界
*return:返回查找的位置下标,返回-1表示未找到
*/
int binarySearch2(element list[], element key, int left, int right)
{
int mid=0;
if(left<=right)// <=,不能遗漏等于的情况
{
mid=left+(right-left)/2;
if(list[mid] == key)
return mid;
else if(list[mid] < key)
return binarySearch2(list, key, mid+1, right);
else
return binarySearch2(list, key, left, mid-1);
}else{
return -1;/*注意如果没有这种情况,返回值是永远是 mid,*/
}
}