排序算法

选择排序

1.直接选择排序

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
  • 算法思想:进行N趟排序,每一趟都从未排序序列中选择一个最大或最小的值放入排序序列中
  • 稳定性:不稳定
template<class KeyType>
int min_key(KeyType A[], int low, int high) //从数据集合中选择最小的关键字,并返回数组下标
{
	int min_pos = low;
	for(int pos=low+1; pos<high; pos++)
	{
		if (A[pos]<A[min_pos])
		{
			min_pos = pos;
		}
	}
	return min_pos;
}
template<class KeyType>
void selection_sort(KeyType A[], int size)
{
	int position;
	for (int i=0; i<size-1; i++)  // 最后一个不用选了
	{
		position = min_key<KeyType>(A, i, size);  //从数据集合中寻找最小的数据
		if (position!=i)
		{
			swap<KeyType>(A[i], A[position]);  //交换两个数据
		}
	}
}


2.堆排序
  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(1)
  • 算法思想:建立一个最大堆或最小堆(堆:以二叉树为数据模型,根结点的数据大于子结点的数据叫做大根堆,反之即为小根堆),每次从堆顶取出一个元素放入目标数组中
  • 稳定性:不稳定
template<class KeyType>
void insert_heap(KeyType A[], KeyType current, int low, int high)  //插入元素到堆中
{
	int large = 2*low + 1;  //计算左边孩子结点的下标
	while(large<high)
	{		
		if (large+1<high && A[large]<A[large+1])//比较两个孩子结点的元素大小
		{
			large++;
		}
		if (current<A[large])  //将插入的元素和较大的孩子结点的元素进行比较
		{
			A[low] = A[large];
			low = large;
			large = 2*low + 1;
		}
		else
		{
			break;
		}
	}
	A[low] = current;
}

template<class KeyType>
void build_heap(KeyType A[], int size)  //将一棵完全二叉树建立成大根堆
{
	for (int low=size/2-1; low>=0; low--)
	{
		insert_heap(A, A[low], low, size);
	}
}

template<class KeyType>
void heap_sort(KeyType A[], int size)
{
	build_heap(A, size);//建立大根堆
	int current;
	for (int i=size-1; i>0; i--)
	{
		current = A[i];//叶子结点的值
		A[i] = A[0];  //将根结点的值放到叶子结点处
		insert_heap(A, current, 0, i);//将叶子结点的值重新插入到堆中
	}
}

插入排序

1.直接插入排序
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
  • 算法思想:遍历数组,将当前元素插入到已排序序列中
  • 算法特点:适合于大部分数据为有序状态的数组排序
  • 稳定性:稳定
template<class KeyType>
void insertion_sort(KeyType A[], int size)
{
	int first_unsorted;  //第一个待排序数据位置
	KeyType current;  //第一个待排序数据
	int high, low;
	for (first_unsorted=1; first_unsorted<size; first_unsorted++)
	{
		if (A[first_unsorted]<A[first_unsorted-1])
		{
			/*current = A[first_unsorted];
			position = first_unsorted;
			do 
			{
				A[position] = A[position-1];
				position--;
			} while (position>0&¤t<A[position-1]);
			A[position] = current;*/
			//采用二分插入排序,减少比较次数
			current = A[first_unsorted];
			low = 0; 
			high = first_unsorted-1;
			while(low<high)                   //寻找插入位置
			{
				int mid = low+(high-low)/2;
				if (A[mid]<=current)
				{
					low = mid+1;
				}
				else
				{
					high = mid-1;
				}
			}
			//移动元素
			for (int i=first_unsorted; i>low; i--)
			{
				A[i] = A[i-1];
			}
			A[low] = current;
		}
	}
}

2.希尔排序
  • 时间复杂度:
  • 空间复杂度:O(1)
  • 算法思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高
  • 算法关键点:如何选择初始增量,以及如何缩减增量
  • 稳定性:稳定
template<class KeyType>
void shell_sort(KeyType A[], int size)
{
	int increment = size;
	int first_unsorted, position;
	KeyType current;
	do 
	{
		increment = increment/3 + 1;  //计算增量,算法的关键点
		for (int i=0; i<increment; i++)  //根据增量分割待排序的元素
		{
			for (first_unsorted=i+increment; first_unsorted<size; first_unsorted+=increment)
			{
				if (A[first_unsorted]<A[first_unsorted-increment]) //在内部用直接插入排序算法进行排序 
				{
					position = first_unsorted;
					current = A[first_unsorted];
					do 
					{
						A[position] = A[position-increment];
						position -= increment;
					} while ((position-increment>=0)&&(current<A[position-increment]));
					A[position] = current;
				}
			}
		}
	} while (increment>1);
}

交换排序

1.快速排序

  • 时间复杂度:平均情况O(NlogN),最差情况(N^2)
  • 空间复杂度:O(1)
  • 算法思想:选择一个枢纽元,数组中小于枢纽元的数据放一边,大于枢纽元的数据放另外一边。不断递归的分割直至数组大小为1
  • 稳定性:不稳定
template<class KeyType>
KeyType median(KeyType A[], int low, int high)
{
	int center = low + (high-low)/2;
	// A[low] <= A[center] <= A[high]
	if (A[center]>A[high]) Swap(A[center], A[high]);
	if (A[low]>A[center]) 
	{
		Swap(A[low], A[center]);
		if (A[center]>A[high]) Swap(A[center], A[high]);
	}
	Swap(A[center], A[high-1]);
	return A[high-1];
}

template<class KeyType>
int partion(KeyType A[], int low, int high)
{
	KeyType pivot = median(A, low, high);  // 三数中值法取枢纽元
	int left = low;
	int right = high-1;
	while (true)
	{
		while(A[++left]<pivot);  // 从左往右找大于pivot的元素
		while(A[--right]>pivot); // 从右往左找小于pivot的元素
		if (left<right) 
			Swap(A[left], A[right]);
		else
			break;
	}
	Swap(A[left], A[high-1]);
	return left;
}

template<class KeyType>
void recursive_quick_sort(KeyType A[], int low, int high)
{
	while (low+1<high)
	{
		int p = partion(A, low, high);
		recursive_quick_sort<KeyType>(A, low, p-1);
		low = p+1;
	}
}

template<class KeyType>
void quick_sort(KeyType A[], int size)
{
	recursive_quick_sort<KeyType>(A, 0, size-1);
	insertion_sort(A, size);
}

2.冒泡排序

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
  • 算法思想:进行N趟排序,每一趟从底部将最大的元素交换到顶部
  • 稳定性:稳定
template<class KeyType>
void bubble_sort(KeyType A[], int size)
{
	bool flag = true; //标记每一趟是否交换过,决定是否提前结束循环
	for (int i=1; i<size&&flag; i++)
	{
		flag = false; //每一趟的开始,设置交换标记为false
		for (int j=0; j<size-i; j++)
		{
			if (A[j]>A[j+1])
			{
				swap<KeyType>(A[j], A[j+1]);
				flag = true;   //设置交换标记为true
			}
		}
	}
}

分配排序

1.基数排序
  • 时间复杂度:O(MN)(M为最大位数)
  • 空间复杂度:O(N)
  • 算法思想:直接看代码
  • 算法局限性:1.数据只能是非负的整数;2.得知道数据的最大位数(十进制)
  • 稳定性:稳定
int maxBit(int A[], int size)//用来求数组中元素的最大位数
{
	int bit = 1;
	int num = 10;
	for (int i=0; i<size; ++i)
	{
		while(A[i]/num>0)
		{
			++bit;
			num *= 10;
		}
	}
	return bit;
}

void radix_sort(int A[], int size)
{
	int bit = maxBit(A, size);//计算数组中元素的最大位数
	int count[10];//计数器
	int * temp = new int[size];//辅助数组
	int i, j, k;
	int radix = 1;
	for (i=1; i<=bit; i++)//进行bit趟排序
	{
		for(j=0; j<10; ++j)
		{
			count[j] = 0;
		}
		for (j=0; j<size; ++j)  //统计每个桶中的记录数
		{
			k = A[j] / radix % 10; //计算所在位上的数字
			++count[k];
		}
		for (j=1; j<10; ++j)
		{
			count[j] += count[j-1];
		}
		for (j=size-1; j>=0; --j)// 将A中的数据存到temp中,从后往前,保持稳定性
		{
			k = A[j] / radix % 10; //计算所在位上的数字
			--count[k];
			temp[count[k]] = A[j];
			
		}
		for (j=0; j<size; ++j)//将temp中的数据复制到A中
		{
			A[j] = temp[j];
		}
		radix *= 10;
	}
	delete [] temp;
}

2.位图排序
  • 时间复杂度:O(N)
  • 空间复杂度:O(MaxValue/32)
  • 算法思想:将每个数据映射到某个数组元素的某一位
  • 算法局限性:1.数据只能是非负的整数;2.数据不能重复;3.须知道最大的数据
  • 算法特点:在数据集合满足条件的情况下,适合查询数据有没有存在
  • 稳定性:数据没有重复,自然是稳定的
//sort the array by bitmap, it demand all elements of src can't be repeated.
const int WORD = 32;
const int SHIFT = 5;  //left shift 5 bits equal to mutiply 32
const int MASK = 0x1f;//31,M%N: if N%2=0, M%N = M&(N-1)
const int MAX_VALUE = 10000000;  //The maximum value of all elements of src.
int bitmap[MAX_VALUE/WORD+1];    //The array of bitmap.

// set the bit
// i right shift 5 bit to calculate the index
// 1<<(i&0x1f) to calculate the bit
void set(int i)
{
	bitmap[i>>SHIFT] |= (1<<(i&MASK));
}

//clear the bit
void clear(int i)
{
	bitmap[i>>SHIFT] &= ~(1<<(i&MASK));
}

//return the result of the bit
int test(int i)
{
	return (bitmap[i>>SHIFT] & (1<<(i&MASK)));
}

void bitmap_sort(int src[], int size)
{
	int i;
	for (i=0; i<MAX_VALUE; ++i)
	{
		clear(i);
	}
	for (i=0; i<size; ++i)
	{
		set(src[i]);
	}
	int j=0;
	for (i=0; i<MAX_VALUE; ++i)
	{
		if (test(i))
		{
			src[j++] = i;
		}
	}
}

3.计数排序
  • 时间复杂度:O(N)
  • 空间复杂度:O(MaxValue)
  • 算法思想:将每个数据映射到某个数组元素的某一位
  • 算法局限性:数据只能是整数
  • 算法特点:在数据集合满足条件的情况下,适合数据集合跨度(Max-Min)不是很大的情况
  • 稳定性:稳定
void count_sort(int A[], size_t size)
{
	if (size>0)
	{
		int max, min;
		max = min = A[0];
		size_t i;
		for(i=1; i<size; ++i)
		{
			if (A[i]>max)
				max = A[i];
			else if (A[i]<min)
				min = A[i];
		}	
		max = max - min + 1;
		size_t* Count = new size_t[max]; // min as offset value
		memset(Count, 0, max*sizeof(size_t));  // initialize
		for (i=0; i<size; ++i)  // count
			++Count[A[i]-min];
	    size_t pos = -1;  // index of A[]
		// sort
		for (i=0; i<max; ++i)  
		{
			while (Count[i]>0)
			{
				A[++pos] = i + min;
				--Count[i];
			}
		}
		delete [] Count;
	}
}

归并排序

1.归并排序

  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(N)
  • 算法思想:分而治之。分:把数组想像成二叉数的根结点,先将该根结点向下逐层分裂成叶结点只含一个元素,这颗树的高度即为logN;治:再从叶结点向上逐层合并,并且在合并的过程中同时排序
  • 稳定性:稳定

template<class KeyType>
void two_merge(KeyType dest[], KeyType src[], int low, int m, int high)  //归并两个有序序列
{
	int i = low;
	int j = m;
	while(i<m&&j<high)  //两个序列中都存在未归并元素
	{
		if (src[i]<=src[j])
		{
			dest[low++] = src[i++]; 
		}
		else
		{
			dest[low++] = src[j++];
		}
	}

	while(i<m)  //第一个有序序列中存在未归并元素
	{
		dest[low++] = src[i++];
	}

	while(j<high) //第二个有序序列中存在未归并元素
	{
		dest[low++] = src[j++];
	}
}

template<class KeyType>
void merge_pass(KeyType dest[], KeyType src[], int len, int size)
{
	int p = 0;  //p指向每一对待归并的第一个元素下标
	while (p+2*len<size)//两两归并长度均为len的有序子序列
	{
		two_merge<KeyType>(dest, src, p, p+len, p+2*len);
		p += 2*len;
	}

	if (p+len<size)  //归并最后两个长度不等的有序子序列
	{
		two_merge<KeyType>(dest, src, p, p+len, size);
	}
	else
	{
		for (int i=p; i<size; i++)
		{
			dest[i] = src[i];          //把剩下的最后一个有序子序列复制到swap中
		}
	}

}

template<class KeyType>
void merge_sort(KeyType A[], int size)
{
	KeyType* temp = new KeyType[size];//构造一个临时数组
	int len = 1;  //有序序列的长度
	while(len<size)
	{
		merge_pass<KeyType>(temp, A, len, size);
		len = len*2;
		merge_pass<KeyType>(A, temp, len, size);
		len = len*2;
	}
	delete [] temp;
}
posted @ 2012-10-02 19:07  刘军newhand_liu  阅读(269)  评论(0编辑  收藏  举报