排序算法

快速排序(基于交换)

特性

每次使一个数归位。不稳定

思想

每次partition划分数组为两部分并返回下标。随后递归地对左右序列快速排序。

代码

int Partition(int arr[],int left,int right)
{
	int i = left;
	int j = right;
	int pivotindex = left; //基准坐标可以任意选
	int pivotnum = arr[pivotindex]; //基准数
	cout << "pivotnum = " << pivotnum << endl;
	while(i < j) //当i==j的时候,i和j的下标就是基准数的正确位置。
	{
		while (i < j && arr[j] >= pivotnum) //先从后面往前找小于基准数
			j--;
		arr[i] = arr[j];   //复制到前面i的位置,不用交换
		while (i < j && arr[i] <= pivotnum)  //再从前往后找大于基准数的值
			i++;
		arr[j] = arr[i]; //复制到后面j的位置。
	}
	arr[i] = pivotnum;  //i,j的位置就是要找的基准数所在的位置。至此数组划分完毕。
	return i;
}
void QuickSort(int arr[], int left,int right)
{
	if (left == right)
		return;
	
		int index = Partition(arr, left, right);
		if(index>left)
		QuickSort(arr, left, index - 1);
		if(index<right)
		QuickSort(arr, index + 1, right);
	
 }

冒泡排序(交换排序)

思想:

两两比较,每次使最小的元素不断往上浮。

特点:

时间复杂度O(N)~O(N^2),空间复杂度为O(1)。稳定,每趟排序使得一个最小的元素归位,全局有序。

代码:

oid bubbleSort(int arr[], int len)
{
	for (int i = 0; i < len-1; i++)
	{
		int flag = 0;//flag记录此次排序有没有交换数字
		for (int j = len - 1; j >= i+1; j--)//从后往前冒泡
		{
			if (arr[j] < arr[j - 1])
			{
				swap(arr[j], arr[j - 1]);
				flag = 1;
			}
		}
		if (flag == 0) //如果本次排序没有交换数字,则表示全局有序。
			return;
	}
}

直接插入排序(插入排序)

思想:

将后面无序数组中的一个待排序的数插入到前面已排序的序列中去。找一个无序的数字,在前面有序的序列中找到插入的位置,移动元素,插入。

特点:

稳定,适用于链表。时间复杂度 O(N)~O(N^2),空间复杂度:O(1)。适用于基本有序或规模小的数组。

代码:

void InsertSort(int arr[], int len)
{
	int i, j;
	for (i = 1; i < len; i++) //从第二个数字开始往前插入。
	{
		int temp;  //保存将要往前插入的数字。
		if (arr[i] < arr[i - 1])//若这个数字本身和前面有序数组放一起是有序的,则跳过。
		{
			j = i;
			temp = arr[i];
			for (; j>0&&temp < arr[j-1]; j--) //从后面往前找插入的位置
			{
				arr[j] = arr[j-1]; //直接覆盖
			}
			arr[j] = temp;
		}
	}
}

折半插入排序(插入排序)

思想:

相比于直接插入排序,折半插入排序将比较和移动操作分离。查找插入的位置用二分法。

特点:

减少了比较的次数,但移动的次数没变,时间复杂度仍未O(N^2)。

代码:

void InsertSort2(int arr[], int len)
{
	int i, j;
	for (i = 1; i < len; i++)
	{
		int temp;
		if (arr[i] < arr[i - 1])
		{
			
			temp = arr[i];
			int low = 0;
			int high = i - 1;
			
			while (low <=high)
			{
				int mid = (low + high) / 2;
				if (arr[i] > arr[mid])
				{
					low = mid + 1;
				}
				else if (arr[i] < arr[mid])
				{
					high = mid - 1;
				}
			}
			for (j = i-1; j >= high+1 ; j--)
			{
				arr[j + 1] = arr[j];
			}
			arr[high + 1] = temp;
		}
	}
}

希尔排序(插入排序)

思想:

分为若干子表分别直接插入排序,最后当总体有序时进行一次直接插入排序。步长逐步减少。不能用于链表。

特点:

时间复杂度:O(N^1.3)(取决于选择的序列),时间复杂度:O(1)。不稳定,不能用于链表。

代码:

void ShellSort(int arr[], int len)
{
	for (int gap = len / 2; gap >= 1; gap = gap / 2) //步长逐渐减小
	{
		for (int i = gap; i < len; i++) //并不需要排完一个子序列再去排下一个子序列。
		{
			if (arr[i] < arr[i - gap]) //无序,说明需要插入到前面的有序数组中
			{
				int temp = arr[i];
				int j;
				for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap)//找插入位置
					arr[j + gap] = arr[j]; //移动元素。
				arr[j + gap] = temp;
			}
		}
	}
}

简单选择排序:

思想:

每次在待排序列表中选一个最小的排到前面去。

特点:

与初始序列无关,时间复杂度始终为O(N^2),空间复杂度为O(1)。不稳定.移动次数小,但是比较次数不变。

代码:

void selectsort(int arr[], int len)
{
	for (int i = 0; i < len - 1; i++)//待排序的数组大小越来越小
	{
		int min = i;  //保存最小数的下标
		for (int j = i + 1; j < len; j++)
		{
			if (arr[j] < arr[min])
				min = j;
			
		}
		if (min != i)
			swap(arr[i], arr[min]); //交换数字
	}
}

堆排序 (选择排序)

思想:按照层次遍历的顺序标号映射到数组中。

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
建立初始堆,输出堆顶元素,将堆底元素送入堆顶,向下调整。

特点:

建堆时间为O(N),之后又n-1次向下调整,每次为O(logN),所以时间复杂度始终为O(NlogN)。不稳定。
删除堆顶元素:把它与堆的最后一个元素交换,向下调整。
插入操作:把新的节点放到堆的末尾,向上调整。

代码:

void adjust(int arr[], int len, int index)
{
    int left = 2 * index + 1; // index的左子节点
    int right = 2 * index + 2;// index的右子节点

    int maxIdx = index;
    if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
    if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;  找左右子节点最大的数交换

    if (maxIdx != index)  //如果将要发生交换
    {
        swap(arr[maxIdx], arr[index]);
        adjust(arr, len, maxIdx);
    }

}

// 堆排序
void heapSort(int arr[], int size)
{
    // 构建大根堆(从最后一个非叶子节点向上)
    for (int i = size / 2 - 1; i >= 0; i--)
    {
        adjust(arr, size, i);
    }

    // 调整大根堆
    for (int i = size - 1; i >= 1; i--)
    {
        swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
        adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
    }
}

归并排序

特性

时间复杂度一直是O(nlogn),稳定,需要辅助数组

思想

采用分治的思想,将数组分为两段,分别归并排序,再合并。

代码

void Merge(int arr[], int l, int mid, int r)  //合并两个数组
{
	int* help = new int[r - l + 1];  //辅助数组
	int p1 = l;   
	int p2 = mid + 1;
	int index = 0;
	while (p1 <= mid && p2 <= r)
	{
		help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
	}
	while (p1 <= mid)  //复制剩余的元素
	{
		help[index++] = arr[p1++];
	}
	while (p2 <= r)
	{
		help[index++] = arr[p2++];
	}
	for (int i = 0; i < r - l + 1; i++) //复制到原数组
	{
		arr[l+i] = help[i];
	}
	delete[] help;
}
void MergeSort(int arr[], int l, int r)
{
	if (l == r)
		return;
	if (l < r)
	{
		int mid = (l + r) / 2;
		MergeSort(arr, l, mid);
		MergeSort(arr, mid+1, r);
		Merge(arr, l, mid, r);
	}
	
}

基数排序

思想:

有LSD(最高位优先),MSD(最高位优先),先按某位数的不同分配到桶中,再收集起来,接着再按下一位分配和收集。

特点:

不依靠比较来排序,稳定。时间复杂度O(d(n+radix))。

原文地址:https://www.cnblogs.com/void-lambda/p/12379954.html