基本思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成
插入流程—L()表示一个元素,L[]表示一个表
实现对L[1…n]的排序
算法实现
void InsertSort(int A[], int n)
{
int i, j;
for(i = 2; i <= n; ++i) //依次将A[2]~A[n]插入到已排序序列
if(A[i] < A[i-1]) //若A[i]关键码小于其前驱,将A[i]插入有序表
{
A[0] = A[i]; //复制为哨兵,A[0]不存放元素
for(j = i-1; A[0] < A[j]; --j)//从后往前查找待插入位置
A[j+1] = A[j]; //向后挪位
A[j+1] = A[0]; //复制到插入位置
}
}
性能分析
思想
算法实现
void InsertSort(int A[], int n)
{
int i, j, low, high, mid;
for(i = 2, i <= n; ++i) //依次将A[2]~A[n]插入前面的已排序序列
{
A[0] = A[i]; //将A[i]暂存到A[0]
low = 1;
high = i-1;
while(low <= high)
{
mid = (low + high) / 2;
if(A[mid] > A[0])
high = mid - 1; //查找左半子表
else
low = mid + 1; //查找右半子表
}
for(j = i-1; j >= high+1; —j)
A[j+1] = A[j]; //统一后移元素,空出插入位置
A[high+1] = A[0];
}
}
性能分析
思想
基本流程
算法实现
void ShellSort(int A[], int n)
{ //A[0]只是暂存单元,不是哨兵,当j<=0时,插入位置已到
for(dk = n/2; dk >= 1; dk = dk/2)
for(i = dk+1; i <= n; ++i)
if(A[i] < A[i-dk]) //需将A[i]插入有序增量子表
{
A[0] = A[i]; //暂存在A[0]
for(j = i-dk; j > 0 && A[0] < A[j]; j -= dk) //记录后移,查找插入位置
A[j+dk] = A[0]; //插入
}
}
性能分析
基本思想:根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置
基本思想
算法实现
void BubbleSort(int A[], int n)
{
int i, j,temp;
bool flag;
for(i = 0; i < n-1; ++i)
{
flag = false; //表示本趟冒泡是否发生交换的标志
for(j = n-1; j > i; --j)//一趟冒泡过程
if(A[j-1] > A[j]) //若为逆序
{
temp = A[j-1]; //交换
A[j-1] = A[j];
A[j] = temp;
flag = true;
}
if(flag == false)
return; //本趟遍历后没有发生交换,说明表已有序
}
}
性能分析
基本思想
算法实现
int Partition(int A[], int low, int high)//一趟划分
{
int pivot = A[low]; //将当前表中第一个元素设为枢轴,对表进行划分
while(low < high)
{
while(low < high && A[high] >= pivot)
--high;
A[low] = A[high]; //将比枢轴小的元素移动到左端
while(low < high && A[low] <= pivot)
++low;
A[high] = A[low]; //将比枢轴大的元素移动到右端
}
A[low] = pivot; //枢轴元素存放到最终位置
return low; //返回存放枢轴的最终位置
}
void QuickSort(int A[], int low, int high)
{
if(low < high) //递归出口
{
int pivotpos = Partition(A, low, high); //划分
QuickSort(A, low, pivotpos-1); //依次对两个子表进行递归排序
QuickSort(A, pivotpos+1, high);
}
}
性能分析
提高效率的方法
1. 编写双向冒泡排序算法,在正反两个方向交替进行扫描,即第一趟把关键字最大的元素放在序列的最后面,第二趟把关键字最小的元素放在序列的最前面,如此反复进行
void BubbleSort(int A[], int n)
{
int low = 0, high = n-1;
int i, temp;
bool flag = true; //一趟冒泡后记录元素是否交换标志
while(low < high && flag) //flag为false说明已没有逆序
{
flag = false;
for(i = low; i < high; ++i)//从前向后冒泡
if(a[i] > a[i+1])
{
temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
flag = true;
}
--high;
for(i = high; i > low; --i)//从后往前冒泡
if(a[i] < a[i-1])
{
temp = a[i];
a[i] = a[i-1];
a[i-1] = temp;
flag = true;
}
++low;
}
}
2. 已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到偶数前边的算法(要求时间最少,辅助空间最少)
void move(int A[], int len)
{
int i = 0, j = len-1;
while(i < j)
{
while(i < j && A[i]%2 != 0)
++i;
while(i < j && A[i]%2 != 1)
--j;
if(i < j)
{
temp = A[i];
A[i] = A[j];
A[j] = temp;
++i, --j;
}
}
}
3. 试编写一个算法,使之能够在数组L[1…n]中找出第k小的元素(即从小到大排序后处于第k个位置的元素)
int kth_elem(int a[], int low, int high, int k)
{
int pivot = a[low];
int low_temp = low; //由于下面会修改low与high,递归时又需要用到它们,故用此暂存
int high_temp = high;
while(low < high)
{
while(low < high && a[high] >= pivot)
--high;
a[low] = a[high];
while(low < high $$ a[low] <= pivot)
++low;
a[high] = a[low];
}
a[low] = pivot;
if(low == k)
return a[low];
else if(low > k)
return kth_elem(a, low_temp, low-1, k);
else
return kth_elem(a, low+1, high_temp, k);
}
4. 已知由n( n ≥ 2 n\ge2 n≥2)个正整数构成的集合 A = { a k ∣ 0 ≤ k < n } A=\{a_k|0\le k
A={ak∣0≤k<n} ,将其划分为两个不相交的子集 A 1 A_1 A1和 A 2 A_2 A2,元素个数分别是 n 1 n_1 n1和 n 2 n_2 n2, A 1 A_1 A1和 A 2 A_2 A2中的元素之和分别是 S 1 S_1 S1和 S 2 S_2 S2,设计一个尽可能高效的划分算法,满足 ∣ n 1 − n 2 ∣ |n_1-n_2| ∣n1−n2∣最小且 ∣ S 1 − S 2 ∣ |S_1-S_2| ∣S1−S2∣最大
5. 设有一个仅有红、白、蓝三种颜色的条块组成的条块序列,请编写一个时间复杂度为 O ( n ) O(n) O(n)的算法,使得这些条块按红、白、蓝的顺序排好
typedef Enum{RED, WHITE, BLUE} color; //设置枚举数组
void Flag_Arrange(color a[], int n)
{
int i = 0, j = 0, k = n-1, temp;
while(j <= k)
switch(a[j]) //判断条块的颜色
{
case RED:
temp = a[i];
a[i] = a[j];
a[j] = temp;
++i,++j;
break;
case WHITE:
++j;
break;
case BLUE:
temp = a[j];
a[j] = a[k];
a[k] = temp;
--k; //没有j++语句,防止交换后a[j]仍是蓝色的情况
}
}
基本思想:每一趟在后面n-i+1(i=1,2,···,n-
1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到n-1趟做完
算法思想:假设排序表为L[1…n],第i趟排序即从L[i…n]中选择关键字最小的元素与L(i)交换,每一趟排序可以确定一个元素的最终位置,经过n-1趟排序就可使得整个排序表有序
算法实现
void SelectSort(int A[], int n)
{
int i, j, temp, min;
for(i = 0; i < n-1; ++i) //一共进行n-1趟
{
min = i; //记录最小元素位置
for(j = i+1; j < n; ++j) //在A[i…n-1]中选择最小的元素
if(A[j] < A[min]) //更新最小元素位置
min = j;
if(min != i) //将当前最小值放到最终位置,共移动3次
{
temp = A[min];
A[min] = A[i];
A[i] = temp;
}
}
}
性能分析
//建立大根堆
void BuildMaxHeap(int A[], int len)
{
for(int i = len/2; i > 0; --i) //从i=[n/2]~1,反复调整堆
HeadAdjust(A, i, len);
}
void HeadAdjust(int A[], int k, int len)
{
A[0] = A[k]; //A[0]暂存子树的根结点
for(i = 2*k, i <= len; i*=2) //沿key较大的子结点向下筛选
{
if(i < len && A[i] < A[i+1])
++i; //取key较大的子结点的下标
if(A[0] >= A[i])
break; //筛选结束
else
{
A[k] = A[i]; //将A[i]调整到双亲结点上
k = i; //修改k值,以便继续向下筛选
}
}
A[k] = A[0]; //被筛选结点的值放入最终位置
}
//堆排序算法
void HeapSort(int A[], int len)
{
BuildMaxHeap(A, len); //初始建堆
for(i = len; i > 1; --i) //n-1趟的交换和建堆过程
{
temp = A[i]; //输出堆顶元素(和堆底元素交换)
A[i] = A[1];
A[1] = temp;
HeadAdjust(A, 1, i-1); //调整,把剩余的i-1个元素整理成堆
}
]
1. 编写一个算法,在基于单链表的待排序关键字序列上进行简单选择排序
void selectSort(LNode *&L)
{
LNode *pre, *p, *max, *maxpre, *h = L->next;
L->next = NULL;
while(h != NULL)
{
p = max = h, pre = maxpre = NULL; //p为工作指针
while(p != NULL)
{
if(p->data > max->data)
{
maxpre = pre; //找到更大的,记忆它和它的前驱
max = p;
}
pre = p;
p = p->next;
}
maxpre->next = max->next; //头插法,将较大的数据先插入,最终得到非递减序列
max->next = L->next;
L->next = max;
}
}
2. 设计一个算法,判断一个数据序列是否构成一个小根堆
bool IsMinHeap(int A[], int len)
{ //数组下标从1开始存储
if(len % 2 == 0) //len为偶数,又一个单分支结点
{
if(A[len/2] > A[len]) //判断单分支结点
return false;
for(i = len/2-1; i >= 1; --i)//判断所有双分支结点
if(A[i] > A[2*i] || A[i] > A[2*i+1])
return false;
}
else //len为奇数时,没有单分支结点
{
for(i = len/2; i >= 1; --i) //判断所有双分支结点
if(A[i] > A[2*i+1] || A[i] > A[2*i+1])
return false;
}
return true;
}
算法实现
int *B = (int *)malloc((n+1)*sizeof(int));
void Merge(int A[], int low, int mid, int high)
{ //表A的两段A[low…mid]和A[mid+1…high]各自有序,将它们合并成一个有序表
for(int k = low; k <= high; ++k)
B[k] = A[k]; //将A中所有元素复制到B中
for(int i = low, j = mid+1, k = i; i <=mid && j <= high; ++k)
{
if(B[i] <= B[j]) //比较B的左右两段中的元素
A[k] = B[i++]; //较较小值复制到A中
else
A[k] = B[j++];
}
while(i <= mid)
A[k++] = B[i++]; //若第一个表未检测完,复制
while(j <= high)
A[k++] = B[j++]; //若第二个表未检测完,复制
}
//合并
void MergeSort(int A[], int low, int high)
{
if(low < high)
{
int mid = (low+high)/2; //从中间划分两个子序列
MergeSort(A, low, mid); //对左侧子序列进行递归排序
MergeSort(A, mid+1, high); //对右侧子序列进行递归排序
Merge(A, low, mid ,high); //归并
}
}
性能分析