您的位置:首页 > 其它

基础算法-交换排序

2016-03-23 10:56 381 查看

基本算法

排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。



常用算法的复杂度稳定性



交换排序

使用交换方式排序的两种排序算法有冒泡排序快速排序

冒泡排序

基本原理

每一个相邻的元素进行比较,较大的元素放置后面,第一趟比较结束可把最大的元素放置到最后一位,第二趟比较结束可把第二大的元素放置到紧挨着最大元素的位置…每一趟都确定一个元素的位置,这样排序结束即可得到有序的序列。

实践

// 简单粗鲁版
void bubbleSort(int a[], int n) {
// 从小到大排序
for (int i = 0; i < n-1; i++) { // 确定a[i]位置上为数组中最小的元素
for (int j = 0; j < n-i-1; j++) { // 注1
// 最大的泡向“上”(右)冒
if (a[j] > a[j+1]) {
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
}

注1:for循环也可以写成 for (int j = 0; j < n-1; j++),但每一趟比较结束后,已经找到找到这一趟最大元素并把它从大到小依次安置在数组的后端,所以n-1-i到n-1的索引的元素已经是从小到大的有序序列,不必再参与比较。


// 别样粗鲁版
void bubbleSortOther(int a[], int n) {
// 从小到大排序
for (int i = 0; i < n; i++) { // 确定a[i]位置上为数组中最小的元素
for (int j = i+1; j < n; j++) { // j从i+1开始,因为i之前位置的元素已经有序
if (a[i] > a[j]) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}


// 微小改进版
// 增加变量exchange,如果某一趟比较检测出剩余元素有序则退出循环
// 注:c语言中没有bool类型,可用定义int exchange=0;0表示false,1表示true

void bubbleSortEx(int a[], int n) {
// 从小到大排序
bool ordered= false;
for (int i = 0; i < n-1 && !ordered; i++) { // 确定a[i]位置上为数组中最小的元素
for (int j = 0; j < n-i-1; j++) {
ordered= true;
// 最大的泡向“上”(右)冒
if (a[j] > a[j+1]) {
ordered= false;
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
}


// 大改版
/*
传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,
考虑利用在每趟排序中进行正向和反向两遍冒泡的方法:
一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。
*/

void bubbleSortEx2(int a[], int n) {
int low = 0;
int high  = n-1;
int tmp, i;
while (low < high) {
// 最大值冒泡
for (i = low; i < high; ++i) { // 注意 i < high
if (a[i] > a[i+1]) {
tmp = a[i];
a[i] = a[i+1];
a[i+1] = tmp;
}
}
--high;
// 最小值冒泡
for (i = high; i > low; --i) {
if (a[i] < a[i-1]){
tmp = a[i];
a[i] = a[i-1];
a[i-1] = tmp;
}
}
++low;
}
}


快速排序

基本思想

每次快排通过“特殊比较方式”确定基准元素(一般选择首元素)的在序列中的最终位置

以基准元素所在位置为中点把序列分割成两个待排序列,每一个序列分别进行快排(使用递归调用), 直到所有的元素都被排序。

快排的特殊比较方式:

步骤1 以序列首元素为基准key,记基准的位置为mid

步骤2 序列从右向左依次与key比较,若遇到元素小于key,则双方进行值交换,更新序列左边位置(left = left+1),更新mid值(mid=right),然后进入第3步;否则更新序列右边位置(right = right-1),进入第2步。

步骤3 序列从左向右依次与key比较,若遇到元素大于key,则双方进行值交换,更新序列右边位置(right = right -1),更新mid值(mid=left),然后进入第2步;否则更新序列左边位置(left = left + 1),进入第3步。

// 直接版
// 大量的 i < j条件,来判断递归是否终止,数组比较是否越界
void quickSort(int a[], int low, int high) {
int i = low;
int j = high;
int mid = 0;
int key = a[i]; // 首元素
if (i < j) { // 因为递归,总要找好结束的条件,你懂得!
while (i < j) {
// 从右向左扫描数组
while (key < a[j] && i < j)
j--;
if (i < j){
a[i] = a[j];
mid = j; // 及时保存mid值,否则i < j时就玩完了
i++;
}

// 从左向右扫描数组
while (key > a[i] && i < j)
i++;
if (i < j) {
a[j] = a[i];
mid = i;
j--;
}
}
a[mid] = key;
quickSort(a, low, mid - 1);
quickSort(a, mid + 1, high);
}
}


// 版本2
// 可作为快排的基本模板使用
void qsort(int array[], int low, int high)
{
if (low >= high)
return;

int key = array[low];
int left = low+1, right = high;

while (left <= right) {
while (left <= right && array[left] < key)
left++;
while (left <= right && array[right] >= key)
right--;

if (left > right)
break;
swap(array[left], array[right]);
}

swap(array[low], array[right]);
qsort(array, low, right-1);
qsort(array, right+1, high);
}


// 版本3
int partition(int *arr,int low,int high)
{
int pivot=arr[high];
int i=low-1;
int j,tmp;
for(j=low;j<high;++j)
if(arr[j]<pivot){
tmp=arr[++i];
arr[i]=arr[j];
arr[j]=tmp;
}
tmp=arr[i+1];
arr[i+1]=arr[high];
arr[high]=tmp;
return i+1;
}
void quick_sort(int *arr,int low,int high)
{
if(low<high){
int mid=partition(arr,low,high);
quick_sort(arr,low,mid-1);
quick_sort(arr,mid+1,high);
}
}


快排的思考

分治法算法是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解。

分治法与快排

很明显,快排就是使用了分治的思想。

1 使用基准key把序列划分成两个子序列,左边的子序列都小于基准key,右边的子序列都大于基准key;这一次划分确定了基准key在排序序列中的最终位置。

2 这两个子序列都是两个独立的子问题,子问题仍然可以使用 1 的方式解决排序的问题。

3 每个划分出的子问题的解决,即所有元素都确定了在排序序列的最终位置,那么整个序列排序的问题就得到了解决。

快排的时间复杂度

单链表快排

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/

ListNode* partion(ListNode* head, ListNode* tail) {
int key = head->val;
ListNode *p = head;
ListNode *q = p->next;

while (q != tail) {
if (q->val < key) {
p = p->next;
swap(p->val, q->val);
}
q = q->next;
}
swap(p->val, head->val);
return p;
}
void quickSort(ListNode* head, ListNode* tail) {
if (head != tail) {
ListNode *pProvet = partion(head, tail);
quickSort(head, pProvet);
quickSort(pProvet->next, tail);
}
}

ListNode* sortList(ListNode* head) {
if (!head || !head->next) return head;

quickSort(head, NULL);
return head;
}


参考资料

1 各种排序算法总结

2 八大排序算法

3 分治

4 快速排序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: