数据结构 C 代码 10.1: 常见的排序算法

数据结构 C 代码 10.1: 常见的排序算法,第1张

摘要: 排序算法最能体现算法的多样性。

1. 代码

先上代码, 再说废话.

/**
 * Hash table.
 * 
 * @author Fan Min minfanphd@163.com.
 */
#include 
#include 

#define TABLE_SIZE 19

/**
 *  pair.
 */
typedef struct Node{
	int key;
	char value;
}Node, *NodePtr;

/**
 *  pair.
 */
typedef struct SequentialList{
	int length;
	NodePtr elements;
}SequentialList, *ListPtr;

/**
 * Initialize a data array.
 */
ListPtr initList(int* paraKeys, char* paraValues, int paraLength){
	int i;
	ListPtr resultPtr = (ListPtr)malloc(sizeof(struct SequentialList));
	resultPtr->length = paraLength;
	resultPtr->elements = (NodePtr)malloc(paraLength * sizeof(struct Node));
	for (i = 0; i < paraLength; i ++){
		//printf("setting key for index %d: %d and value: %c\r\n", i, paraKeys[i], paraValues[i]);
		resultPtr->elements[i].key = paraKeys[i];
		resultPtr->elements[i].value = paraValues[i];
	}//Of for i

	return resultPtr;
}//Of initList

/**
 * Print the list.
 */
void printList(ListPtr paraPtr) {
	int i;
	printf("(Keys, values)\r\n");
	for (i = 0; i < paraPtr->length; i ++) {
		printf("%d\t", paraPtr->elements[i].key);
	}//Of for i
	printf("\r\n");
	for (i = 0; i < paraPtr->length; i ++) {
		printf("%c\t", paraPtr->elements[i].value);
	}//Of for i
}//Of printList

/**
 * Insertion sort. paraPtr->elements[0] does not store a valid data. paraPtr->elements[0].key should
 * be smaller than any valid key.
 */
void insertionSort(ListPtr paraPtr) {
	Node tempNode;
	int j;
	for (int i = 2; i < paraPtr->length; i++) {
		tempNode = paraPtr->elements[i];
		
		//Find the position to insert.
		//At the same time, move other nodes.
		for (j = i - 1; paraPtr->elements[j].key > tempNode.key; j--) {
			paraPtr->elements[j + 1] = paraPtr->elements[j];
		} // Of for j
		
		//Insert.
		paraPtr->elements[j + 1] = tempNode;
	} // Of for i
}// Of insertionSort

/**
 * Test the method.
 */
void insertionSortTest() {
	int tempUnsortedKeys[] = { -100, 5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = { 'n', 'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 8);

	printf("\r\nBefore insertion sort:\r\n");
	printList(tempList);

	insertionSort(tempList);
	printf("\r\nAfter insertion sort:\r\n");
	printList(tempList);
}// Of insertionSortTest
 
/**
 * Shell sort.
 */
void shellSort(ListPtr paraPtr) {
	Node tempNode;
	int tempJumpArray[] = { 5, 3, 1 };
	int tempJump;
	int p, i, j, k;
	for (i = 0; i < 3; i++) {
		tempJump = tempJumpArray[i];
		for (j = 0; j < tempJump; j++) {
			for (k = j + tempJump; k < paraPtr->length; k += tempJump) {
				tempNode = paraPtr->elements[k];
				// Find the position to insert.
				// At the same time, move other nodes.
				for (p = k - tempJump; p >= 0; p -= tempJump) {
					if (paraPtr->elements[p].key > tempNode.key) {
						paraPtr->elements[p + tempJump] = paraPtr->elements[p];
					} else {
						break;
					} // Of if
				} // Of for p

				// Insert.
				paraPtr->elements[p + tempJump] = tempNode;
			} // Of for k
		} // Of for j
	} // Of for i
}// Of shellSort

/**
 * Test the method.
 */
void shellSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore shell sort:\r\n");
	printList(tempList);

	shellSort(tempList);
	printf("\r\nAfter shell sort:\r\n");
	printList(tempList);
}// Of shellSortTest

/**
 * Bubble sort.
 */
void bubbleSort(ListPtr paraPtr) {
	bool tempSwapped;
	Node tempNode;
	int i, j;
	for (i = paraPtr->length - 1; i > 0; i--) {
		tempSwapped = false;
		for (j = 0; j < i; j++) {
			if (paraPtr->elements[j].key > paraPtr->elements[j + 1].key) {
				// Swap.
				tempNode = paraPtr->elements[j + 1];
				paraPtr->elements[j + 1] = paraPtr->elements[j];
				paraPtr->elements[j] = tempNode;

				tempSwapped = true;
			} // Of if
		} // Of for j

		// No swap in this round. The data are already sorted.
		if (!tempSwapped) {
			printf("Premature.\r\n");
			break;
		} // Of if
	} // Of for i
}// Of bubbleSort

/**
 * Test the method.
 */
void bubbleSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore bubble sort:\r\n");
	printList(tempList);

	shellSort(tempList);
	printf("\r\nAfter bubble sort:\r\n");
	printList(tempList);
}// Of bubbleSortTest

/**
 * Quick sort recursive.
 */
void quickSortRecursive(ListPtr paraPtr, int paraStart, int paraEnd) {
	int tempPivot, tempLeft, tempRight;
	Node tempNodeForSwap;
	
	// Nothing to sort.
	if (paraStart >= paraEnd) {
		return;
	} // Of if

	tempPivot = paraPtr->elements[paraEnd].key;
	
	tempLeft = paraStart;
	tempRight = paraEnd - 1;

	// Find the position for the pivot.
	// At the same time move smaller elements to the left and bigger one to the
	// right.
	while (true) {
		while ((paraPtr->elements[tempLeft].key < tempPivot) && (tempLeft < tempRight)) {
			tempLeft++;
		} // Of while

		while ((paraPtr->elements[tempRight].key >= tempPivot) && (tempLeft < tempRight)) {
			tempRight--;
		} // Of while

		if (tempLeft < tempRight) {
			// Swap.
			//System.out.println("Swapping " + tempLeft + " and " + tempRight);
			tempNodeForSwap = paraPtr->elements[tempLeft];
			paraPtr->elements[tempLeft] = paraPtr->elements[tempRight];
			paraPtr->elements[tempRight] = tempNodeForSwap;
		} else {
			break;
		} // Of if
	} // Of while

	// Swap
	if (paraPtr->elements[tempLeft].key > tempPivot) {
		tempNodeForSwap = paraPtr->elements[paraEnd];
		paraPtr->elements[paraEnd] = paraPtr->elements[tempLeft];
		paraPtr->elements[tempLeft] = tempNodeForSwap;
	} else {
		tempLeft++;
	} // Of if

	//System.out.print("From " + paraStart + " to " + paraEnd + ": ");
	//System.out.println(this);

	quickSortRecursive(paraPtr, paraStart, tempLeft - 1);
	quickSortRecursive(paraPtr, tempLeft + 1, paraEnd);
}// Of quickSortRecursive

/**
 * Quick sort.
 */
void quickSort(ListPtr paraPtr) {
	quickSortRecursive(paraPtr, 0, paraPtr->length - 1);
}// Of quickSort

/**
 * Test the method.
 */
void quickSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore quick sort:\r\n");
	printList(tempList);

	quickSort(tempList);
	printf("\r\nAfter quick sort:\r\n");
	printList(tempList);
}// Of quickSortTest

/**
 * Selection sort.
 */
void selectionSort(ListPtr paraPtr) {
	Node tempNode;
	int tempIndexForSmallest, i, j;

	for (i = 0; i < paraPtr->length - 1; i++) {
		// Initialize.
		tempNode = paraPtr->elements[i];
		tempIndexForSmallest = i;
		for (j = i + 1; j < paraPtr->length; j++) {
			if (paraPtr->elements[j].key < tempNode.key) {
				tempNode = paraPtr->elements[j];
				tempIndexForSmallest = j;
			} // Of if
		} // Of for j

		// Change the selected one with the current one.
		paraPtr->elements[tempIndexForSmallest] = paraPtr->elements[i];
		paraPtr->elements[i] = tempNode;
	} // Of for i
}// Of selectionSort

/**
 * Test the method.
 */
void selectionSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore selection sort:\r\n");
	printList(tempList);

	selectionSort(tempList);
	printf("\r\nAfter selection sort:\r\n");
	printList(tempList);
}// Of selectionSortTest

/**
 * Adjust heap.
 */
void adjustHeap(ListPtr paraPtr, int paraStart, int paraLength) {
	Node tempNode = paraPtr->elements[paraStart];
	int tempParent = paraStart;
	int tempKey = paraPtr->elements[paraStart].key;

	for (int tempChild = paraStart * 2 + 1; tempChild < paraLength; tempChild = tempChild * 2 + 1) {
		// The right child is bigger.
		if (tempChild + 1 < paraLength) {
			if (paraPtr->elements[tempChild].key < paraPtr->elements[tempChild + 1].key) {
				tempChild++;
			} // Of if
		} // Of if

		//System.out.println("The parent position is " + tempParent + " and the child is " + tempChild);
		if (tempKey < paraPtr->elements[tempChild].key) {
			// The child is bigger.
			paraPtr->elements[tempParent] = paraPtr->elements[tempChild];
			//System.out.println("Move " + paraPtr->elements[tempChild].key + " to position " + tempParent);
			tempParent = tempChild;
		} else {
			break;
		} // Of if
	} // Of for tempChild

	paraPtr->elements[tempParent] = tempNode;
}// Of adjustHeap
 
 /**
 * Heap sort.
 */
void heapSort(ListPtr paraPtr) {
	Node tempNode;
	int i;
	// Step 1. Construct the initial heap.
	for (i = paraPtr->length / 2 - 1; i >= 0; i--) {
		adjustHeap(paraPtr, i, paraPtr->length);
	} // Of for i

	// Step 2. Swap and reconstruct.
	for (i = paraPtr->length - 1; i > 0; i--) {
		tempNode = paraPtr->elements[0];
		paraPtr->elements[0] = paraPtr->elements[i];
		paraPtr->elements[i] = tempNode;

		adjustHeap(paraPtr, 0, i);
		//System.out.println("Round " + (length - i) + ": " + this);
	} // Of for i
}// Of heapSort

/**
 * Test the method.
 */
void heapSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore heap sort:\r\n");
	printList(tempList);

	heapSort(tempList);
	printf("\r\nAfter heap sort:\r\n");
	printList(tempList);
}// Of heapSortTest

/**
 * Selection sort.
 */
void mergeSort(ListPtr paraPtr) {
	// Step 1. Allocate space.

	int tempRow; // The current row
	int tempGroups; // Number of groups
	int tempActualRow; // Only 0 or 1
	int tempNextRow = 0;
	int tempGroupNumber;
	int tempFirstStart, tempSecondStart, tempSecondEnd;
	int tempFirstIndex, tempSecondIndex;
	int tempNumCopied;
	int i;
	int tempSize;

	/*
	ListPtr tempMatrix[2];
	int* tempIntArray = (int*)malloc(paraPtr->length * sizeof(int));
	char* tempCharArray = (char*)malloc(paraPtr->length * sizeof(char));
	tempMatrix[0] = paraPtr;
	tempMatrix[1] = initList(tempIntArray, tempCharArray, paraPtr->length);
	*/

	Node** tempMatrix = (Node**)malloc(2 * sizeof(Node*));
	tempMatrix[0] = (Node*)malloc(paraPtr->length * sizeof(Node));
	tempMatrix[1] = (Node*)malloc(paraPtr->length * sizeof(Node));
	for (i = 0; i < paraPtr->length; i ++) {
		tempMatrix[0][i] = paraPtr->elements[i];
	}//Of for i

	/*
	Node tempMatrix[2][length];

	// Step 2. Copy data.
	for (i = 0; i < length; i++) {
		tempMatrix[0][i] = data[i];
	} // Of for i
	*/

	// Step 3. Merge. log n rounds
	tempRow = -1;
	for (tempSize = 1; tempSize <= paraPtr->length; tempSize *= 2) {
		// Reuse the space of the two rows.
		tempRow++;
		//System.out.println("Current row = " + tempRow);
		tempActualRow = tempRow % 2;
		tempNextRow = (tempRow + 1) % 2;

		tempGroups = paraPtr->length / (tempSize * 2);
		if (paraPtr->length % (tempSize * 2) != 0) {
			tempGroups++;
		} // Of if
		//System.out.println("tempSize = " + tempSize + ", numGroups = " + tempGroups);

		for (tempGroupNumber = 0; tempGroupNumber < tempGroups; tempGroupNumber++) {
			tempFirstStart = tempGroupNumber * tempSize * 2;
			tempSecondStart = tempGroupNumber * tempSize * 2 + tempSize;
			if (tempSecondStart > paraPtr->length - 1) {
				// Copy the first part.
				for (i = tempFirstStart; i < paraPtr->length; i++) {
					tempMatrix[tempNextRow][i] = tempMatrix[tempActualRow][i];
				} // Of for i
				continue;
			} // Of if
			tempSecondEnd = tempGroupNumber * tempSize * 2 + tempSize * 2 - 1;
			if (tempSecondEnd > paraPtr->length - 1) {
				tempSecondEnd = paraPtr->length - 1;
			} // Of if

			tempFirstIndex = tempFirstStart;
			tempSecondIndex = tempSecondStart;
			tempNumCopied = 0;
			while ((tempFirstIndex <= tempSecondStart - 1)
					&& (tempSecondIndex <= tempSecondEnd)) {
				if (tempMatrix[tempActualRow][tempFirstIndex].key <= tempMatrix[tempActualRow][tempSecondIndex].key) {

					tempMatrix[tempNextRow][tempFirstStart
							+ tempNumCopied] = tempMatrix[tempActualRow][tempFirstIndex];
					tempFirstIndex++;
					//System.out.println("copying " + tempMatrix[tempActualRow][tempFirstIndex]);
				} else {
					tempMatrix[tempNextRow][tempFirstStart
							+ tempNumCopied] = tempMatrix[tempActualRow][tempSecondIndex];
					//System.out.println("copying " + tempMatrix[tempActualRow][tempSecondIndex]);
					tempSecondIndex++;
				} // Of if
				tempNumCopied++;
			} // Of while

			while (tempFirstIndex <= tempSecondStart - 1) {
				tempMatrix[tempNextRow][tempFirstStart
						+ tempNumCopied] = tempMatrix[tempActualRow][tempFirstIndex];
				tempFirstIndex++;
				tempNumCopied++;
			} // Of while

			while (tempSecondIndex <= tempSecondEnd) {
				tempMatrix[tempNextRow][tempFirstStart
						+ tempNumCopied] = tempMatrix[tempActualRow][tempSecondIndex];
				tempSecondIndex++;
				tempNumCopied++;
			} // Of while
		} // Of for groupNumber

	} // Of for tempStepSize

	for (i = 0; i < paraPtr->length; i ++) {
		paraPtr->elements[i] = tempMatrix[tempNextRow][i];
	}//Of for i
}// Of mergeSort

/**
 * Test the method.
 */
void mergeSortTest() {
	int tempUnsortedKeys[] = {5, 3, 6, 10, 7, 1, 9 };
	char tempContents[] = {'i', 't', 'e', 's', 'c', 'f', 'w' };
	ListPtr tempList = initList(tempUnsortedKeys, tempContents, 7);

	printf("\r\nBefore merge sort:\r\n");
	printList(tempList);

	mergeSort(tempList);
	printf("\r\nAfter merge sort:\r\n");
	printList(tempList);
}// Of mergeSortTest
 
 /**
 * The entrance of the program.
 */
int main() {
	insertionSortTest();
	//shellSortTest();
	//bubbleSortTest();
	//quickSortTest();
	//selectionSortTest();
	//heapSortTest();
	//mergeSortTest();
	return 1;
}// Of main
2. 运行结果
Before insertion sort:
(Keys, values)
-100    5       3       6       10      7       1       9
n       i       t       e       s       c       f       w
After insertion sort:
(Keys, values)
-100    1       3       5       6       7       9       10
n       f       t       i       e       c       w       s       Press any key to continue
3. 代码说明

好几个算法.

3.1 插入排序
  1. 插入排序是简单直接的排序方式之一. 代码非常短.
  2. 每次保证前 i 个数据是有序的.
  3. 先做简单的事情 (第 1 轮最多有 1 次移动), 再做麻烦的事情 (最后一轮最多有 n − 1 n - 1n−1 次移动).
  4. 下标 0 的数据为岗哨. 比其它排序方式多用一个空间.
3.2 希尔排序
  1. 多达 4 重循环, 但时间复杂度只有 O ( n 2 ) O(n^2 ) O(n2).
  2. 多次排序反正减少了平均排序时间. 神奇的脑回路.
  3. 有了昨天的程序铺垫, 本程序写起来也不难.
  4. 岗哨的个数与最初的步长相关, 我们的程序中为 5. 简便起见我就没用了.
  5. 可以改变 tempJumpArray.
3.3 冒泡排序
  1. 每次确定当前最大值, 也就是确定一个位置的数据.
  2. 仅交换相邻数据.
  3. 如果某一趟没有交换, 就表示数据已经有序 (早熟, premature), 可以提前结束了.
3.4 快速排序
  1. 平均时间复杂度为 O ( n log ⁡ ⁡ n ) O(n \log ⁡ n ) O(nlogn), 但最坏情况还是 O ( n 2 ) O(n^2) O(n2).
  2. Pivot 应该选 (该子序列的) 最后一个元素.
  3. 递归算法, 每次只能确定 pivot 的位置.
  4. 判断条件 && (tempLeft < tempRight) 不能少.
  5. (paraPtr->elements[tempRight].key >= tempPivot) 不能写成 >, 否则出现两个相同 key 时可能出错.
3.5 选择排序
  1. 又是一个基础 (简单) 算法.
  2. 与插入排序不同, 先做最麻烦的, 要进行 n − 1 n − 1 n1 次比较才能获得最小的数据.
  3. 数据一旦被选择并确定位置, 就不再改变.
  4. 作为一种简单算法, 其时间复杂度为 O ( n 2 ) O(n^2) O(n2).
  5. 只需要两个额外的空间来存放最小数据的引用与下标, 因此空间复杂度为 O ( 1 ) O(1) O(1).
3.6 堆排序
  1. 堆排序可能是排序算法中最难的. 用到了二叉树.
  2. 建初始堆比较费劲.
  3. 调整堆的时间复杂度为 O ( log ⁡ ⁡ n ) O(\log ⁡ n) O(logn), 所以总体时间复杂度只有 O ( n log ⁡ ⁡ n ) O(n \log ⁡ n) O(nlogn).
  4. 空间复杂度只有 O ( 1 ) O(1) O(1).
3.7 归并排序
  1. log ⁡ n \log n logn 轮, 每轮 O ( n ) O(n) O(n) 次拷贝. 因此时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn).
  2. 空间复杂度为 O ( n ) O(n) O(n). 只需要一行辅助空间.
  3. 全都是在拷贝引用, 而不是数据本身. 这是 Java 的特性.
  4. 里面的两重循环总共只有 O ( n ) O(n) O(n). 这里是分成了若干个小组.
  5. 归并两个有序小组的时候, 用了三个并列的循环.
  6. 涉及分组后尾巴的各种情况, 所以需要相应的 if 语句.

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1329722.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存