对于排序算法来说,需要对代码实现烂熟于胸,知道空间消耗、平均时间复杂度和最差时间复杂度。
稳定的只有:直接插入、冒泡、归并、基数排序。
private
static
int
[] insertSort(
int
[]arr){
if
(arr ==
null
|| arr.
length
< 2){
return
arr;
}
for
(
int
i=1;i<arr.
length
;i++){
for
(
int
j=i;j>0;j--){
if
(arr[j]<arr[j-1]){
//
TODO
:
int
temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
else
{
//接下来是无用功
break
;
}
}
}
return
arr;
}
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
public
static
void
shellsort(
int
[] a){
int
d = a.
length
;
while
(
true
){
for
(
int
i=0;i<d;i++){
for
(
int
j=i;j+d<a.
length
;j+=d){
int
temp;
if
(a[j]>a[j+d]){
temp=a[j];
a[j]=a[j+d];
a[j+d]=temp;
}
}
}
if
(d==1){
break
;}
d--;
}
}
3.选择:
public
static
void
selectSort
(
int
[]a)
{
int
minIndex=0;
int
temp=0;
if
((a==
null
)||(a.
length
==0))
return
;
for
(
int
i=0;i<a.
length
-1;i++)
{
minIndex=i;
//无序区的最小数据数组下标
for
(
int
j=i+1;j<a.
length
;j++)
{
//在无序区中找到最小数据并保存其数组下标
if
(a[j]<a[minIndex])
{
minIndex=j;
}
}
if
(minIndex!=i)
{
//如果不是无序区的最小值位置不是默认的第一个数据,则交换之。
temp=a[i];
a[i]=a[minIndex];
a[minIndex]=temp;
}
}
}
4.堆排序:堆排序是一种选择排序。是不稳定的排序方法。时间复杂度为O(nlog2n)。
堆是一种重要的
数据结构,为一棵
完全二叉树, 底层如果用数组存储数据的话,假设某个元素为序号为i(
Java数组从0开始,i为0到n-1),
如果它有左子树,那么左子树的位置是2
i+1,如果有右子树,右子树的位置是2
i+2,如果有父节点,父节点的位置是(n-1)/2取整。
其基本思想是
1、将要排序的数组创建为一个大根堆。大根堆的堆顶元素就是这个堆中最大的元素。
2、将大根堆的堆顶元素和无序区最后一个元素交换,并将无序区最后一个位置例入有序区,然后将新的无序区调整为大根堆。
3、重复操作,无序区在递减,有序区在递增。
初始时,整个数组为无序区,第一次交换后无序区减一,有序区增一。
每一次交换,都是大根堆的堆顶元素插入有序区,所以有序区保持是有序的。
P.S.
大根堆和小根堆
堆:是一颗完全二叉树。
大根堆:所有节点的子节点比其自身小的堆
小根堆:所有节点的子节点比其自身大的堆
public static void heapS(int[] arr){
if(arr == null || arr.length == 0)
return;
int len = arr.length;
for(int i = (len - 1) / 2; i >= 0; i--){
buildMaxHeap(arr,i,len);
}
for(int i = len - 1; i > 0; i--){
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
buildMaxHeap(arr,0,i);
}
}
public static void buildMaxHeap(int[] arr, int start, int len){
int key = arr[start];
for(int i=2*start+1; i<len-1; i=2*i+1){ //i为初始化为节点k的左孩子,沿节点较大的子节点向下调整
if(i<len && arr[i]<arr[i+1]){ //取节点较大的子节点的下标
i++; //如果节点的右孩子>左孩子,则取右孩子节点的下标
}
if(key >= arr[i]){ //根节点 >=左右子女中关键字较大者,调整结束
break;
}else{ //根节点 <左右子女中关键字较大者
arr[start] = arr[i]; //将左右子结点中较大值array[i]调整到双亲节点上
start = i; //【关键】修改k值,以便继续向下调整
}
}
arr[start] = key; //被调整的结点的值放人最终位置
}
5.快排:时间空间复杂度都是O(nlogn),不稳定。
适用于:
被排序的数据完全无序
public
static
void
quicksort(
int
[] arr,
int
low,
int
high){
if
(low < high){
int
mid =
getMiddle
(arr,low,high);
quicksort
(arr,low,mid-1);
quicksort
(arr,mid+1,high);
}
}
public
static
int
getMiddle(
int
[] arr,
int
low,
int
high){
int
key = arr[low];
while
(low < high){
while
(low < high && arr[high] >= key)
high--;
arr[low] = arr[high];
while
(low < high && arr[low] <= key)
low++;
arr[high] = arr[low];
}
arr[low] = key;
return
low;
}
6.冒泡
public
void
bubbleSort(
int
[] array){
for
(
int
i=0;i<array.
length
;i++){
for
(
int
j=0;j<array.
length
-1-i;j++){
if
(array[j]>array[j+1]){
int
tmp
= array[j];
Array[j] = array[j+1];
Array[j+1] = tmp;
//交换j和j+1
}
}
}
}
7.归并:时间复杂度是O(nlogn),空间复杂度
O(n)
,稳定的。
将已有序的子序列合并,得到完全有序的序列。将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
public
static
void
sort(
int
[] data,
int
left,
int
right) {
if
(left >= right)
return
;
// 找出中间索引
int
center = (left + right) / 2;
// 对左边数组进行递归
sort
(data, left, center);
// 对右边数组进行递归
sort
(data, center + 1, right);
// 合并
merge
(data, left, center, right);
}
public
static
void
merge(
int
[] data,
int
left,
int
center,
int
right) {
// 临时数组
int
[] tmpArr =
new
int
[data.
length
];
// 右数组第一个元素索引
int
mid = center + 1;
// third 记录临时数组的索引
int
third = left;
// 缓存左数组第一个元素的索引
int
tmp = left;
while
(left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if
(data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
}
else
{
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
while
(mid <= right) {
tmpArr[third++] = data[mid++];
}
while
(left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原left-right范围的内容被复制回原数组)
while
(tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
8.基数:
public static void sort(int[] number, int d) //d表示最大的数有多少位
{
intk = 0;
intn = 1;
intm = 1; //控制键值排序依据在哪一位
int[][]temp = newint[10][number.length]; //数组的第一维表示可能的余数0-9
int[]order = newint[10]; //数组orderp[i]用来表示该位是i的数的个数
while(m <= d)
{
for(inti = 0; i < number.length; i++)
{
intlsd = ((number[i] / n) % 10);
temp[lsd][order[lsd]] = number[i];
order[lsd]++;
}
for(inti = 0; i < 10; i++)
{
if(order[i] != 0)
for(intj = 0; j < order[i]; j++)
{
number[k] = temp[i][j];
k++;
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
}
9.折半查找方法适用于不经常变动而查找频繁的有序列表。