Tri rapide, tri Hill, tri par fusion, tri par tas, tri par insertion, tri par bulles, tri par sélection (récursif, non récursif) Langage C détaillé

1. Le concept de tri et son application

1.1 Le concept de tri

Tri : Le tri dit est l'opération de classement d'une chaîne d'enregistrements par ordre croissant ou décroissant selon la taille d'un ou plusieurs des mots-clés.

Stabilité : Supposons qu'il y ait plusieurs enregistrements avec le même mot-clé dans la séquence d'enregistrements à trier. S'ils sont triés, l'ordre relatif de ces enregistrements reste inchangé, c'est-à-dire, dans la séquence d'origine, r[i]=r[j] , et r[i] est avant r[j], et dans la séquence triée, r[i] est encore avant r[j], l'algorithme de tri est dit stable, sinon il est dit instable.

Tri interne : Un tri dans lequel toutes les données sont placées en mémoire.

Tri externe : trop d'éléments de données ne peuvent pas être placés en mémoire en même temps, et le tri des données ne peut pas être déplacé entre la mémoire interne et externe selon les exigences du processus de tri.

1.2 Demande de tri
1.3 Algorithmes de tri courants
// 排序实现的接口

// 插入排序
void InsertSort(int* a, int n);

// 希尔排序
void ShellSort(int* a, int n);

// 选择排序
void SelectSort(int* a, int n);

// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);

// 冒泡排序
void BubbleSort(int* a, int n)

// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right);
// 快速排序挖坑法
int PartSort2(int* a, int left, int right);
// 快速排序前后指针法
int PartSort3(int* a, int left, int right);
void QuickSort(int* a, int left, int right);

// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)

// 归并排序递归实现
void MergeSort(int* a, int n)
// 归并排序非递归实现
void MergeSortNonR(int* a, int n)

// 计数排序
void CountSort(int* a, int n)

// 测试排序的性能对比
void TestOP()
{
     
     
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int)*N);
	int* a2 = (int*)malloc(sizeof(int)*N);
	int* a3 = (int*)malloc(sizeof(int)*N);
	int* a4 = (int*)malloc(sizeof(int)*N);
	int* a5 = (int*)malloc(sizeof(int)*N);
	int* a6 = (int*)malloc(sizeof(int)*N);

	for (int i = 0; i < N; ++i)
	{
     
     
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];

	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort(a5, 0, N-1);
	int end5 = clock();

	int begin6 = clock();
	MergeSort(a6, N);
	int end6 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
}
Trier le JO (vous pouvez utiliser différents tris pour exécuter ce JO) Lien JO

2. Implémentation d'algorithmes de tri communs

2.1 Tri par insertion
2.1.1 Idée de base :

Le tri par insertion directe est une méthode simple de tri par insertion dont l'idée de base est :

Insérez les enregistrements à trier dans une séquence triée un par un en fonction de la taille de leurs valeurs clés, jusqu'à ce que tous les enregistrements soient insérés et qu'une nouvelle séquence soit obtenue .

En pratique, quand on joue au poker, on utilise la notion de tri par insertion

2.1.2 Tri par insertion directe :

Lors de l'insertion du i-ème (i>=1) élément, les précédents array[0], array[1],...,array[i-1] ont été triés, et à ce moment utilisez le code de tri de array [i] et Array[i-1], array[i-2],... comparent l'ordre des codes de tri, trouvent la position d'insertion et insèrent array[i], et l'ordre des éléments à la position d'origine est reculé

Récapitulatif des caractéristiques du tri par insertion directe :

  1. Plus l'ensemble d'éléments est proche de l'ordre, plus l'efficacité temporelle de l'algorithme de tri par insertion directe est élevée.
  2. Complexité temporelle : O(N^2)
  3. Complexité spatiale : O(1), c'est un algorithme de tri stable
  4. Stabilité : stable
//插入排序
void InsertSort(int* a, int length)
{
     
     
	for (int i = 1; i < length; i++)
	{
     
     
		int end = i - 1;
		int num = a[i];
		while (end >= 0)
		{
     
     
			if (num < a[end])
			{
     
     
				a[end + 1] = a[end];//挪动数组
				end--;
			}
			else
			{
     
     
				break;//找到了要插入的点
			}
		}
		a[end + 1] = num;
	}
}
2.1.3 Tri en colline (réduction du tri incrémental)

La méthode de tri de Hill est également connue sous le nom de méthode d'incrément de rétrécissement. L'idée de base de la méthode de tri Hill est la suivante : sélectionnez d'abord un nombre entier, divisez tous les enregistrements du fichier à trier en groupes, et regroupez tous les enregistrements avec une distance de 0 dans le même groupe, et triez les enregistrements dans chaque groupe. . Ensuite, prenez et répétez le travail de regroupement et de tri ci-dessus. Lorsque reach = 1, tous les enregistrements sont triés dans le même groupe .

4

//希尔排序
void ShellSort(int* a, int length)
{
     
     
	//接近有序
	int gap = length;
	while (gap > 1)
	{
     
     
		gap /= 2;
		for (int i = 0; i < length - gap; i++)
		{
     
     
			int end = i;
			int num = a[i + gap];
			while (end >= 0)
			{
     
     
				if (a[end] > num)
				{
     
     
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
     
     
					break;
				}
			}
			a[end + gap] = num;
		}
	}
}

Résumé des caractéristiques du tri Hill :

  1. Le tri Hill est une optimisation du tri par insertion directe.

  2. Lorsque l'écart> 1, il est pré-trié, le but est de rendre le tableau plus proche de l'ordre. Lorsque gap == 1, le tableau est déjà proche de l'ordre, il sera donc très rapide. De cette manière, l'effet d'optimisation global peut être atteint. Après l'avoir implémenté, nous pouvons comparer les tests de performance.

  3. La complexité temporelle du tri de Hill n'est pas facile à calculer, car il existe de nombreuses façons d'évaluer l'écart, ce qui le rend difficile à calculer. Par conséquent, la complexité temporelle du tri de Hill donnée dans de nombreux arbres n'est pas fixe :

"Structure de données (édition en langage C)" - Yan Weimin

"Structure de données - Utilisation de la méthode orientée objet et de la description C++" - Yin Renkun

Parce que notre écart est évalué selon la méthode proposée par Knuth, et que Knuth a réalisé un grand nombre de statistiques expérimentales, nous suivons temporairement : O ( n 1,25 ) O(n^{1,25})O ( n1,25 )àO ( 1,6 ∗ n 1,25 ) O(1,6*n^{1,25})O ( 1,6n1.25 )Calcul futur.

  1. Stabilité : Instable
2.2 Tri de sélection

2.2.1 Idée de base :

Sélectionnez l'élément le plus petit (ou le plus grand) parmi les éléments de données à trier à chaque fois, et stockez-le au début de la séquence jusqu'à ce que tous les éléments de données à trier soient épuisés.

2.2.2 Tri par sélection directe :
  • Sélectionnez l'élément de données avec la clé la plus grande (la plus petite) dans l'ensemble d'éléments array[i]–array[n-1]

  • Si ce n'est pas le dernier (premier) élément de l'ensemble, échangez-le avec le dernier (premier) élément de l'ensemble

  • Dans la collection restante array[i]–array[n-2] (array[i+1]–array[n-1]), répétez les étapes ci-dessus jusqu'à ce qu'il reste 1 élément dans la collection

//选择排序
void SelectSort(int* a, int length)
{
     
     
	int left = 0, right = length - 1;
	while (left < right)
	{
     
     
		int maxi = left, mini = left;
		for (int i = left + 1; i <= right; i++)
		{
     
     
			if (a[i] < a[mini])
			{
     
     
				mini = i;
			}
			if (a[i] > a[maxi])
			{
     
     
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]);
		if (left == maxi) maxi = mini;
		Swap(&a[right], &a[maxi]);
		left++;
		right--;
	}
}
Résumé des fonctionnalités du tri par sélection directe :
  1. La pensée de tri par sélection directe est très facile à comprendre, mais l'efficacité n'est pas très bonne. rarement utilisé en pratique
  2. Complexité temporelle : O(N^2)
  3. Complexité spatiale : O(1)
  4. Stabilité : Instable
2.2.3 Tri par tas

Le tri en tas (Heapsort) fait référence à un algorithme de tri conçu à l'aide d'une structure de données arborescente empilée (tas), qui est un type de tri par sélection. Il sélectionne les données dans le tas. Il convient de noter que vous devez créer un grand tas pour l'ordre croissant et un petit tas pour l'ordre décroissant.

6

//堆排序
void AdjustDown(int* a, int sz, int parent)
{
     
     
	//调大堆
	assert(a);
	int child = parent * 2 + 1;
	while (child < sz)//儿子节点要存在
	{
     
     
		//找左右儿子中最大的那个
		if (child + 1 < sz && a[child] < a[child + 1])
		{
     
     
			child++;//找到了最大的那个
		}

		if (a[child] > a[parent])
		{
     
     
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
     
     
			break;
		}
	}
}

void SetHeap(int* a, int sz)
{
     
     
	assert(a);
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
     
     
		AdjustDown(a, sz, i);
	}
}

void heap_sort(int* a, int sz)
{
     
     
	int num = sz;
	SetHeap(a, sz);
	while (num)
	{
     
     
		Swap(&a[0], &a[num-1]);
		num--;
		AdjustDown(a, num, 0);
	}
}
Résumé des fonctionnalités du tri par sélection directe :
  1. Le tri par tas utilise le tas pour sélectionner des nombres, ce qui est beaucoup plus efficace.
  2. Complexité temporelle : O(N*logN)
  3. Complexité spatiale : O(1)
  4. Stabilité : Instable
2.3 Permuter le tri

Idée de base: Le soi-disant échange consiste à échanger les positions des deux enregistrements dans la séquence en fonction du résultat de la comparaison des valeurs clés des deux enregistrements dans la séquence. Les enregistrements avec des valeurs clés plus petites sont déplacés vers le devant la séquence.

2.3.1 Tri à bulles
//冒泡排序
void BubbleSort(int* a, int n)
{
     
     
	for (int i = 0; i < n - 1; i++)
	{
     
     
		bool exchange = false;
		for (int j = 0; j < n - i - 1; j++)
		{
     
     
			if (a[j] > a[j + 1])
			{
     
     
				Swap(&a[j], &a[j + 1]);
				exchange = true;
			}
		}
		if (exchange == false)
		{
     
     
			break;
		}
	}
}

Résumé des caractéristiques du tri à bulles :

  1. Le tri à bulles est un tri très facile à comprendre
  2. Complexité temporelle : O(N^2)
  3. Complexité spatiale : O(1)
  4. Stabilité : stable
2.3.2 Tri rapide

Le tri rapide est une méthode de tri par échange de structure arborescente binaire proposée par Hoare en 1962. Son idée de base est la suivante : tout élément de la séquence d'éléments à trier est pris comme valeur de référence, et l'ensemble à trier est divisé en deux sous-séquences selon au code de tri, tous les éléments de la sous-séquence de gauche sont inférieurs à la valeur de référence, tous les éléments de la sous-séquence de droite sont supérieurs à la valeur de référence, puis la sous-séquence la plus à gauche répète le processus jusqu'à ce que tous les éléments soient disposés dans la position correspondante .

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
     
     
  if(right - left <= 1)
      return;

  // 按照基准值对array数组的 [left, right)区间中的元素进行划分
  int div = partion(array, left, right);

  // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
  // 递归排[left, div)
  QuickSort(array, left, div);

  // 递归排[div+1, right)
  QuickSort(array, div+1, right);
}

Ce qui précède est le cadre principal pour la réalisation récursive du tri rapide. On constate qu'il est très similaire aux règles de parcours de préordre de l'arbre binaire. Lors de l'écriture du cadre récursif, les élèves peuvent réfléchir aux règles de parcours de préordre de l'arbre binaire et écrivez-le rapidement Le post-ordre n'a qu'à analyser comment suivre la valeur de référence La façon de diviser les données dans l'intervalle est suffisante.

Les méthodes courantes pour diviser l'intervalle en moitiés gauche et droite en fonction de la valeur de base sont :

  1. version rauque
//hoare版本
void QuickSort(int* a, int left,int right)
{
     
     
	if (left >= right) return;
	
	int begin = left, end = right;
	int keyi = left;
	int mid = GetMid(a, left, right);
	Swap(&a[left], &a[mid]);
	while (left < right)
	{
     
     
		while (left < right && a[right] >= a[keyi])
		{
     
     
			right--;
		}

		while (left < right && a[left] <= a[keyi])
		{
     
     
			left++;
		}

		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = right;
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
  1. creuser une fosse
//挖坑法
void QuickSort(int* a, int left, int right)
{
     
     
	if (left >= right) return;
	
	int key = a[left];
	int begin = left, end=right;
	while (left < right)
	{
     
     
		while (left < right && a[right] >= key)
		{
     
     
			right--;
		}
		a[left] = a[right];
		
		while (left < right && a[left] <= key)
		{
     
     
			left++;
		}
		a[right] = a[left];
	}
	a[left] = key;
	int keyi = left;
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
  1. Version pointeur avant et arrière
//前后指针快速排序
void QuickSort(int* a, int left, int right)
{
     
     
	if (left >= right)
	{
     
     
		return;
	}
	
	int cur = left + 1, prev = left;
	int keyi = left;
	while (cur <= right)
	{
     
     
		if (a[cur] < a[keyi] && cur > prev)
		{
     
     
			prev++;
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	int mid = prev;
	QuickSort(a, left, mid - 1);
	QuickSort(a, mid+1, right);
}

2.3.2 Optimisation du tri rapide

  1. Trois nombres prennent la méthode du milieu pour choisir la clé
int GetMid(int* a,int left,int right)
{
     
     
	int mid = left + right >> 1;
	if (a[left] < a[mid])
	{
     
     
		if (a[mid] < a[right])
		{
     
     
			return mid;
		}
		else if (a[left] > a[right])
		{
     
     
			return left;
		}
		else
		{
     
     
			return right;
		}
	}
	else//a[left]>a[mid]
	{
     
     
		if (a[mid] > a[right])
		{
     
     
			return mid;
		}
		else if (a[left] < a[mid])
		{
     
     
			return left;
		}
		else
		{
     
     
			return right;
		}
	}
}
  1. Lors de la récurrence dans de petites sous-gammes, pensez à utiliser le tri par insertion
//小区间优化
void QuickSort(int* a, int left, int right)
{
     
     
	if (left >= right)
	{
     
     
		return;
	}
	if ((right - left + 1) > 10)
	{
     
     
		int cur = left + 1, prev = left;
		int keyi = left;
		while (cur <= right)
		{
     
     
			if (a[cur] < a[keyi] && cur > prev)
			{
     
     
				prev++;
				Swap(&a[cur], &a[prev]);
			}
			cur++;
		}
		Swap(&a[prev], &a[keyi]);
		int mid = prev;
		QuickSort(a, left, mid - 1);
		QuickSort(a, mid + 1, right);
	}
	else
	{
     
     
		InsertSort(a + left, right - left + 1);
	}
}
2.3.2 Tri rapide non récursif
void QuickSortNonR(int* a, int left, int right)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, left);
	StackPush(&st, right);

	while (StackEmpty(&st) != 0)
	{
		right = StackTop(&st);
		StackPop(&st);
		left = StackTop(&st);
		StackPop(&st);

      if(right - left <= 1)
          continue;

		int div = PartSort1(a, left, right);
       // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
      StackPush(&st, div+1);
	    StackPush(&st, right);

		StackPush(&st, left);
		StackPush(&st, div);
	}

  StackDestroy(&s);
}
int OnceSort(int* a, int left, int right)
{
     
     
	if (left > right)
	{
     
     
		return;
	}
	int key = a[left];
	while (left < right)
	{
     
     
		//先算右边,右边找大
		while (left<right&&a[right] >= key)
		{
     
     
			right--;
		}
		//找到了就交换
		a[left] = a[right];
		while (left<right&&a[left] <= key)
		{
     
     
			left++;
		}
		a[right] = a[left];
	}
	a[left] = key;//将key放在正确的位置上
	int meeti = left;//相遇的点
	return meeti;
}

void QuickSort(int* a, int left, int right)
{
     
     
	ST st;//创建一个栈来模拟递归的过程
	STInit(&st);
	STPush(&st,right);
	STPush(&st,left);
	while (!STEmpty(&st))
	{
     
     
		//左区间
		int begin = STTop(&st);
		STPop(&st);
		int end = STTop(&st);
		STPop(&st);
		int mid = OnceSort(a, begin, end);
       
		if(end > mid + 1)
		{
     
     
			STPush(&st, end);
			STPush(&st, mid + 1);
		}
       //如果left>=mid-1说明左边已经排完序了
       if(begin < mid - 1)
		{
     
     
			STPush(&st,mid - 1);
			STPush(&st, begin);
		}
	}
	STDestroy(&st);
}

ifPourquoi la condition de l'énoncé du jugement n'est-elle pas prise ici =?

Si on prend le signe égal :

Il y aura de nombreux jugements inutiles. Lorsque début et fin sont égaux, il n'y a qu'un seul élément. Un élément n'a pas besoin d'être trié, il n'est donc pas nécessaire de prendre un =nombre

Si aucun =numéro n'est pris :

Résumé des fonctionnalités de tri rapide :
  1. Les performances globales et les scénarios d'utilisation du tri rapide sont relativement bons, j'ose donc l'appeler tri rapide

  2. Complexité temporelle : O(N*logN)

15

  1. Complexité spatiale : O(logN)

  2. Stabilité : Instable

2.4 Tri par fusion

Idée basique:

Le tri par fusion (MERGE-SORT) est un algorithme de tri efficace basé sur l'opération de fusion, qui est une application très typique de diviser pour régner (Divide and Conquer). Combinez les sous-séquences ordonnées pour obtenir une séquence complètement ordonnée ; c'est-à-dire, faites d'abord chaque sous-séquence dans l'ordre, puis faites les segments de la sous-séquence dans l'ordre. La fusion de deux listes triées en une seule liste triée s'appelle une fusion bidirectionnelle.
Les étapes principales de la fusion et du tri : [Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-svQBGJGJ-1683775858015) (C:/Users/ 19735/Bureau/%E6%95% B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%9D%E9%98%B6V5-2021%E4%BF%AE%E8 %AE%A2/Leçon6–%E6 %8E%92%E5%BA%8F/12.jpg)]

//归并排序递归
void _MergeSort(int* a, int begin, int end, int* tmp)
{
     
     
	if (begin >= end)
	{
     
     
		return;
	}
	
	int mid = begin + end >> 1;
	_MergeSort(a, begin, mid,tmp);
	_MergeSort(a, mid+1, end,tmp);
	
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
     
     
		if (a[begin1] < a[begin2])
		{
     
     
			tmp[i++] = a[begin1++];
		}
		else
		{
     
     
			tmp[i++] = a[begin2++];
		}
	}
	
	//处理剩余的
	while (begin1 <= end1)
	{
     
     
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
     
     
		tmp[i++] = a[begin2++];
	}

	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

void MergeSort(int* a, int sz)
{
     
     
	int* tmp = (int*)malloc(sizeof(int) * sz);
	if (tmp == NULL)
	{
     
     
		perror("malloc fail");
		exit(-1);
	}
	_MergeSort(a, 0, sz - 1, tmp);
	
	free(tmp);
}
//归并排序非递归
void MergeSortNonR(int* a, int n)
{
     
     
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
     
     
		perror("malloc fail");
		exit(-1);
	}
	int gap = 1;//分组,组间距
	while (gap < n)
	{
     
     
		for (int i = 0; i < n; i += gap * 2)
		{
     
     
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;
			if (begin1>=n||end1 >= n || begin2 >= n)
			{
     
     
				break;
			}

			if (end2 >= n)
			{
     
     
				end2 = n - 1;
			}
			printf("[%d %d] [%d %d]\n", begin1, end1, begin2, end2);
			while (begin1 <= end1 && begin2 <= end2)
			{
     
     
				if (a[begin1] < a[begin2])
				{
     
     
					tmp[j++] = a[begin1++];
				}
				else
				{
     
     
					tmp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
     
     
				tmp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
     
     
				tmp[j++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(tmp);
}

Si l'intervalle n'est pas corrigé :

On peut voir que le tableau d'origine n'a à l'origine que 9 éléments, et la plage effective est [0,8]mais dans l'opération ci-dessus, il y a évidemment une plage qui dépasse cette plage, alors quelle en est la raison ?

Le code dans la case franchira la limite lorsque l'écart est grand, mais le programme continuera car begin2il est encore inférieur àend2

Processus d'exécution du code :

Résumé des caractéristiques du tri par fusion :
  1. L'inconvénient de la fusion est qu'elle nécessite une complexité d'espace O(N), et la réflexion sur la fusion et le tri vise davantage à résoudre le problème du tri externe dans le disque.
  2. Complexité temporelle : O(N*logN)
  3. Complexité spatiale : O(N)
  4. Stabilité : stable
2.5 Tri sans comparaison

Idée : Le tri par comptage, également connu sous le nom de principe du casier, est une application modifiée de la méthode d'adressage direct par hachage.
Pas:

  1. Compter le nombre d'occurrences d'un même élément
  2. Recyclez la séquence à la séquence d'origine en fonction des résultats statistiques13
//计数排序
void CountSort(int* a, int n)
{
     
     
	int max = a[0], min = a[0];
	for (int i = 1; i < n; ++i)
	{
     
     
		if (a[i] > max)
		{
     
     
			max = a[i];
		}

		if (a[i] < min)
		{
     
     
			min = a[i];
		}
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int) * range);
	if (countA == NULL)
	{
     
     
		perror("malloc fail\n");
		return;
	}
	memset(countA, 0, sizeof(int) * range);

	// 计数
	for (int i = 0; i < n; i++)
	{
     
     
		countA[a[i] - min]++;
	}

	// 排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
     
     
		while (countA[i]--)
		{
     
     
			a[j++] = i + min;
		}
	}

	free(countA);
}
Résumé des fonctionnalités du tri par comptage :
  1. Le tri par comptage est très efficace lorsque la plage de données est concentrée, mais son champ d'application et ses scénarios sont limités.
  2. Complexité temporelle : O(MAX(N,plage))
  3. Complexité de l'espace : O(gamme)
  4. Stabilité : stable

3. Analyse de la complexité et de la stabilité des algorithmes de tri

16

17

4. Pratique à choix multiples

1. 快速排序算法是基于(    )的一个排序算法。
A 分治法
B 贪心法
C 递归法
D 动态规划法

2.对记录(54,38,96,23,15,72,60,45,83)进行从小到大的直接插入排序时,当把第8个记录45插入到有序表时,为找到插入位置需比较( )次?(采用从后往前比较)
A 3
B 4
C 5
D 6

3.以下排序方式中占用O(n)辅助存储空间的是
A 选择排序
B 快速排序
C 堆排序
D 归并排序

4.下列排序算法中稳定且时间复杂度为O(n2)的是( )
A 快速排序
B 冒泡排序
C 直接选择排序
D 归并排序

5.关于排序,下面说法不正确的是
A 快排时间复杂度为O(N*logN),空间复杂度为O(logN)
B 归并排序是一种稳定的排序,堆排序和快排均不稳定
C 序列基本有序时,快排退化成冒泡排序,直接插入排序最快
D 归并排序空间复杂度为O(N), 堆排序空间复杂度的为O(logN)

6.下列排序法中,最坏情况下时间复杂度最小的是( )
A 堆排序
B 快速排序
C 希尔排序
D 冒泡排序

7.设一组初始记录关键字序列为(65,56,72,99,86,25,34,66),则以第一个关键字65为基准而得到的一趟快速排序结果是()
A 3456256586997266
B 2534566599867266
C 3456256566998672
D 3456256599867266
答案:
1.A
2.C
3.D
4.B
5.D
6.A
7.A

Je suppose que tu aimes

Origine blog.csdn.net/AkieMo/article/details/130591011
conseillé
Classement