[算法 – C / C ] 快速排序 – 问题


首先我们学习的想法算法

这个想法: 快速排序通过比较该列表与所选择的元件的每个元件分成两个列表数组称为闩锁元件. 较小的元件或闩锁元件被提出并在第一列表, 主要内容包括更大的后和列表中的第二个孩子上. 这种划分继续,直到列表中有相同的长度 1

内容
1. 该算法的想法
2. 安装快速排序
3. 减少递归快速排序
4. 在快速排序使用尾递归
5. 在C ++中的快速排序可使用
6. 排序阵列结构的快速排序

有选择以下关键要素:
– 选择元素使头或站端闭锁元件.
– 选择要素站在关键要素列表之间做.
– 选择中间元素 3 元件头, 站立和在底部的关键元件.

– 随机选择一个元素作为关键要素 (或单程被选择用于避免的特殊情况)

插图:

快速排序

安装在c:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>		// thu vien de khoi tao tham so cho ham rand()
#define swap(type, a, b) {type temp = a; a = b; b = temp; } // hang hoan vi

void quickSort(int *a, int l, int r)
{
	srand(time(NULL));	//khoi tao tham so ham rand()
	int key = a[l + rand() % (r-l+1)];	//lay khoa la gia tri ngau nhien tu a[l] -> a[r]
	//int key = a[(l+r)/2];
	int i = l, j = r;

	while(i <= j)
	{
		while(a[i] < key) i++;		// tim phan tu ben trai ma >=key
		while(a[j] > key) j--;		// tim phan tu ben trai ma <=key
		if(i <= j)
		{
			if (i < j)
				swap(int, a[i], a[j]);	// doi cho 2 phan tu kieu int a[i], a[j].
			i++;
			j--;
		}
	}
	//bay gio ta co 1 mang : a[l]....a[j]..a[i]...a[r]
	if (l < j) quickSort(a, l, j);	// lam lai voi mang a[l]....a[j]
	if (i < r) quickSort(a, i, r); // lam lai voi mang a[i]....a[r]
}

int main ()
{
	int i, arr[] = { 40, 10, 100, 90, 20, 25 };
	quickSort(arr, 0, 5);
	for (i=0; i<6; i++)
		printf ("%d ", arr[i]);
	return 0;
}

的算法被从线安装 6 以行 29. 包括线路 21 互换(INT, 该[在], 该[Ĵ]); 是一个常数置换 2 所以[在] 和[Ĵ] 符合规定 4 它是等效于以下代码 (你可以直接将其更改为):

{
	int temp = a[i];
	a[i] = a[j];
	a[j] = temp;
}

您还可以看到下面的代码, 与此代码我已经打印出来的画面数组中的每个变化, 升, ř, 在, Ĵ帮助他们改正步骤观察到的变化:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>		// thu vien de khoi tao tham so cho ham rand()
#define swap(type, a, b) {type temp = a; a = b; b = temp; } // hang hoan vi

void quickSort(int *a, int l, int r)
{
	srand(time(NULL));	//khoi tao tham so ham rand()
	int key = a[l + rand() % (r-l+1)];	//lay khoa la gia tri ngau nhien tu a[l] -> a[r]
	//int key = a[(l+r)/2];
	int i = l, j = r;
	printf("\n\nl = %d   r = %d    select: key = %d  (random) ",l, r, key);

	while(i <= j)
	{
		//printf("n");
		printf("\n\n\ti : %d", i);
		while (a[i] < key) { i++; printf(" -> %d",i); }
		printf("\n\tj : %d", j);
		while (a[j] > key) { j--; printf(" -> %d",j); }
		if(i <= j)
		{
			if (i < j)
			{
				swap(int, a[i], a[j]);	// doi cho 2 phan tu kieu int a[i], a[j].
				printf("\n\tswap(a[%d], a[%d])  swap(%d, %d)", i, j, a[i], a[j]);
			}
			i++;
			j--;
			printf("\n\tarr[] : ");
			for (int i=0; i<8; i++)
				printf ("%d ", a[i]);
		}
	}
	//bay gio ta co 1 mang : a[l]....a[j]..a[i]...a[r]
	if (l < j) quickSort(a, l, j);	// lam lai voi mang a[l]....a[j]
	if (i < r) quickSort(a, i, r); // lam lai voi mang a[i]....a[r]
}

int main ()
{
	int i, arr[] = { 2, 8, 7, 1, 3, 5, 6, 4  };
	int n = 8;
	printf("\n\nArray befor sort: ");
	for (i=0; i<n; i++)
		printf ("%d ", arr[i]);

	quickSort(arr, 0, n-1);

	printf("\n\nArray after sort: ");
	for (i=0; i<n; i++)
		printf ("%d ", arr[i]);
}

我注意到算法的有效性取决于所选择的值的标记 (或闭锁元件).

– 最好的情况下,: 每一次我们选择的分区中位数元素 (较大的颗粒或由元件数量的一半并且小于或等于一半的剩余元件的) 作为一个具有里程碑意义. 同时套房被分成相等的两部分, 我们需要LOG2(ñ) 分区一旦安排是完全. 这也是每个分区中可见,我们需要浏览n个元素. 因此,复杂性澳最好的情况下(nlog2(ñ)).

– 最糟糕的情况: 每个业主必须规划元素,我们选择作为一个具有里程碑意义的最大值或最小值. 当该序列被划分为两个不平坦部: 部分只有一个元素, 其余有n-1个元素. 因此, n次,我们需要安排新的分区完成. 因此,复杂性澳最坏的情况下(N2).

* 因此,我们有以下快速排序的复杂性:

– 最好的情况下,: 该(nlog2n)
– 最糟糕的情况: 该(N2)
– 平均情况下: 该(nlog2n)

消除递归QUIC排序

递归算法参数的性质存储在栈递归 (堆) 转动手柄检索. 因此,如果您遇到大量的数据将很容易导致堆栈溢出. 当减少递归递归算法, 在我的快速排序每个调用递归函数由左和右的保存值代替 2 在子 2 堆栈Sl的VA锶, 当被叫停. 此外,我们还可以存储共用的左侧和右侧中的值 1 堆, 取出来的时候将采取 2 连续元素.

B1: 初始化 2 堆栈Sl的荣, SR保存每个子阵列的值L和R值.
B2: 最初,从问题的范围 0 到n-1的, 我注意到从左至右的SL和SR
B3: l在Sl的滚出, 从锶 - [R
B4: 分区A系列[L] .. 该[ř] 到 2 这里[升]..该[Ĵ] 和A[在]..该[ř]
B5: 如果l < 升和jĴ存储在S1和锶, 如果我 < R I和R存储在S1和锶,
B6: 如果栈不空回B3.

这里安装了代码, 该代码可以使用以 堆仲C ++, 如果你不想要或不熟悉C ++可以参照建筑栈 1 堆栈始终.

#include<stdio.h>
#include<stdlib.h>
#include<time.h>		// thu vien de khoi tao tham so cho ham rand()
#include<stack> 		// Su dung Stack trong C++
#define swap(type, a, b) {type temp = a; a = b; b = temp; }

using namespace std;

void quickSortUnRecursive(int *a, int l, int r)
{
	srand(time(NULL));
	stack Sl;		// Stack left
	stack Sr;		// Stack right
	Sl.push(l);			// dua phan tu dau tien l vao Sl
	Sr.push(r);			// dua phan tu dau tien r vao Sl

	while(!Sl.empty())	// trong khi stack khong rong
	{

		int l = Sl.top(); Sl.pop();		// lay phan tu dau tien trong Sl l ra
		int r = Sr.top(); Sr.pop();		// lay phan tu dau tien trong Sr r ra
		int key = a[l + rand() % (r-l+1)];
		int i = l, j = r;
		while (i <= j)		// phan hoach thanh 2 day con
		{
			while (a[i] < key) i++;
			while (a[j] > key) j--;

			if(i <= j)
			{
				if (i < j)
					swap(int, a[i], a[j]);
				i++;
				j--;
			}
		}
		if (l < j) { Sl.push(l); Sr.push(j); }	// dua 2 dau mut l va j vao Sl va Sr
		if (i < r) { Sl.push(i); Sr.push(r); }	// dua 2 dau mut i va r vao Sl va Sr
	}
}

int main ()
{
	int i, arr[] = { 40, 10, 100, 90, 20, 25 };
	quickSortUnRecursive(arr, 0, 5);
	for (i=0; i<6; i++)
		printf ("%d ", arr[i]);
	return 0;
}


使用尾递归的快速排序

我们也可以用尾递归算法来构建快速排序算法, 函数,即只有一个递归调用, 但要做到这一点,我们将不得不选择的关键元素是数组的第一个元素应持续或安排. 我们可以分析这种情况如下:

首先,我们确定的分布范围成 2 以下数组:

int partition(int *a, int l, int r)
{
	int key = a[r];		// xac dinh khoa
	int i = l - 1, j;	// khoi tao i, j
	for (j=l; j<r; j++)	// duyet mang
		if (a[j] <= key) //neu a[j] <= key
		{
			i++;
			swap(int, a[i], a[j]);	// hoan vi
		}
	swap(int, a[i+1], a[r]);	// hoan vi voi phan tu khoa
	return i + 1;	// tra ve vi tri cua khoa
}

快速排序尾递归

除以我们的范围后, 2 排, 范围的左边是更小的元素或键, 右边是大关键要素的数组.

现在我们如下建立快速排序尾递归:

void TailRecursiveQuicksort(int *a, int l, int r)
{
	while (l<r)
	{
		int q = partition(a,l,r);	//lay vi tri khoa
		TailRecursiveQuicksort(a, l, q-1);		// sap xep day ben trai
		l = q + 1;		// khi day ben trai sap xep xong ta chuyen sang day ben phai
	}
}

而且, 目的使递归调用时, 我们的堆叠可能含有尽可能少, 所以我们进行了一些改进的算法如下:

void TailRecursiveQuicksort_2(int *a, int l, int r)
{
	while (l<r)
	{
		int q = partition(a,l,r);
		if (q < (l + (r-l)/2))	//Neu vi tri khoa < 1/2 do dai day thi sap xep ben phai
		{
			TailRecursiveQuicksort_2(a, l, q-1);
			l = q + 1;
		}
		else	//Neu vi tri khoa >= 1/2 do dai day thi sap xep ben trai
		{
			TailRecursiveQuicksort_2(a, q + 1, r);
			r = q-1;
		}
	}
}


使用快速以C ++排序

在C ++中已经为我们建造qsort函数,我们可以立即使用它的图书馆.

#include<stdio.h>
#include<stdlib.h>

int arr[] = { 40, 10, 100, 90, 20, 25 };

int compare (const void * a, const void * b)
{
  return ( *(int*)a > *(int*)b );
}

int main ()
{
	  int n;
	  qsort (arr + 1, 4, sizeof(int), compare);
	  for (n=0; n<6; n++)
		 printf ("%d ",arr[n]);
	  return 0;
}

在函数调用的qsort: 的qsort (ARR + 1, 4, 的sizeof(INT), 比较);
ARR + 1 是起始位置阵列排列, 这里是相当于一种元素的[1] = 10 和 安排 4 元素 即从[1] 到[4].
的sizeof(INT) 尺寸 1 数组中元素. 如果数组是实际类型将有sizeof的(浮动).
比较 建用下面的结构被用于确定所述阵列被布置向上或向下

int compareMyType (const void * a, const void * b)
{
	return ( *(MyType*)a @ *(MyType*)b );
}

其中之一是数组元素的MyType类型. “@” ]是定义一个操作. 在库中已定义了我们 3 操作 >, ==, <. 在这个例子中我们使用 *(INT *)该 > *(INT *)b,来安排有增加, 如果反之亦然 *(INT *)B > *(INT *)将安排优惠.

排序在C语言中快速排序阵列结构++

接下来我们会做的,如果我们有 1 结构元素数组结构体,并希望排序 1 某些字段.

我将定义 1 操作 “@” 比较 1 颌骨的某些领域 布尔运算符 然后重建功能 INT比较 钢铁业务已建成:

#include<iostream>
#include<cstdlib>
using namespace std;

struct St
{
	string name;
	int scores;
};

bool operator * ( const St &t, const St &u )	// dinh nghia phep toan "*"
{
	return t.name > u.name;	//sap xep tang dan theo ten
}

bool operator/( const St &t, const St &u )	// dinh nghia phep toan "/"
{
	return t.scores < u.scores;	//sap xep giam dan theo diem
}

int compare_name (const void * a, const void * b)
{
	return ( *( St*)a * *(St*)b );	//chi duoc su dung phep toan "*" de sap xep
}

int compare_scores (const void * a, const void * b)
{
	return ( *( St*)a / *(St*)b );	//chi duoc su dung phep toan "/" de sap xep
}

int main ()
{
	St arr[] = { {"Hien", 9}, {"Cuong", 7}, {"Nhung", 8}, {"Nam", 6} };

	int n = sizeof( arr ) / sizeof( St );
	qsort (arr, n, sizeof(St), compare_name);

	cout << "\nDanh sach xap xep tang dan theo ten:\n";
	for (int i = 0; i < n; i++ )
		cout << arr[ i ].name << ' ' << arr[ i ].scores << endl;

	qsort (arr, n, sizeof(St), compare_scores);
	cout << "\nDanh sach xap xep giam dan theo diem:\n";
	for (int i = 0; i < n; i++ )
		cout << arr[ i ].name << ' ' << arr[ i ].scores << endl;

	return 0;
}

在上面的代码中,我使用特殊符号 * 和 / (但更精确的正常符号用作乘除) 来定义安排操作. 我们可以用一些其他的操作,比如人物 %, ^, *, /, +, -, >, =, < 定义新的运算.

从一些网站上的文章引用和翻译:
recurial.com/programming/using-tail-recursion/
mypathtothe4.blogspot.com/2013/02/lesson-2-variations-on-quicksort-tail.html
本书: 介绍的算法
staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap08.htm
cplusplus.com/reference/cstdlib/qsort/?千瓦=的qsort