目录
测试80000数据排序的时间的代码: 耗时在标题上写着,这个和个人计算机硬件有关,仅参考
相关术语解释:
1)
稳定
:如果
a
原本在
b
前面,而
a=b
,排序之后
a
仍然在
b
的前面;
2)
不稳定
:如果
a
原本在
b
的前面,而
a=b
,排序之后
a
可能会出现在
b
的后面;
3)
内排序
:所有排序操作都在内存中完成;
4)
外排序
:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
●
5)
时间复杂度:
一个算法执行所耗费的时间。
6)
空间复杂度
:运行完一个程序所需内存的大小。
7)
n:
数据规模
8)
k:
“
桶”的个数
9)
In-place:
不占用额外内存
10)
Out-place:
占用额外内存
测试80000数据排序的时间的代码: 耗时在标题上写着,这个和个人计算机硬件有关,仅参考
int arr[] = new int[80000];
for (int i = 0;i< 80000;i++){
arr[i] = (int) (Math.random()*800000);
}
long l = System.currentTimeMillis();
BubbleSort.bobbleSort(arr);
long l1 = System.currentTimeMillis();
System.out.println("花费时间:"+(l1-l)+ "ms");
一、冒泡排序:9521ms
public static void bobbleSort(int arr[]) {//冒泡排序时间复杂度为O(n^2)
//下标指向第一个数,把他和下一个数比较,大的放后面,小的放前面,然后下标向后挪一位,继续这样比较,一轮下来确定了一个最大的,
// 然后下标从新指向第一个数,再比较一轮,这里的结束条件是上一轮的基础上减去1.然后这样下去即可)
boolean flag = false;
for (int j = 0; j < arr.length - 1; j++) {//当有5个数时,需要遍历4组
for (int i = 0; i < arr.length - 1 - j; i++) {//每次遍历比上一次要少遍历一个数
int temp = 0;
if (arr[i] > arr[i + 1]) {
flag = true;//判断一组数据是否已经是顺序的
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
if (flag == true) {//如果一轮循环解说后发生了交换,则再吧flag改回去,如果这一轮没有发生交换,证明已经是顺序的了,则可以减少循环直接退出即可
flag = false;
} else {
break;
}
}
}
二、选择排序:2444ms
public static void selectSort(int[] arr) {//先把第一个当作最小的,和后面的比,比一圈下来,最小的那个数和下标记录下来,
//和第一个数交换位置,然后就群定了一个数,依次向后一个一个确定下来即可)
for (int j = 0; j < arr.length - 1; j++) {//总共循环 arr.length-1 次
int min = arr[j];
int minIndex = j;
for (int i = j + 1; i < arr.length; i++) {//这里找到最小的元素
if (min > arr[i]) {
min = arr[i];
minIndex = i;
}
}
//最小元素放在最前面
if (minIndex != j) {//这里的判断会节省时间
arr[minIndex] = arr[j];
arr[j] = min;
}
}
}
三、插入排序:542ms
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int insertVal = arr[i];//带插入的数
int insertIndex = i - 1;//带插入的数的前一个数的下标
//给insertVal找位置,这个太厉害了 (把要插入的数拿出来和前面的一个一个比,
// 前面的数要是大,就往后挪一位,前面的数要是小或者相等要是小,那就跟你后面了)
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//退出while时表示找到了
arr[insertIndex + 1] = insertVal;
// System.out.println(Arrays.toString(arr));
}
}
四、希尔排序:118ms
希尔排序:交换法
public static void shellSort(int arr[]) {
int temp = 0;
//共有gap组,
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j + gap];
arr[j + gap] = arr[j];
arr[j] = temp;
}
}
}
}
}
对希尔排序优化:移位法
public static void shellSort2(int arr[]) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {//设置步长,依次除以2
for (int i = gap; i < arr.length; i++) {//此时i指向每一个小组中的第二个元素,i不断增长,一直是两两比较,
// 即使一组有很多个元素也是和自己减去步长的那个元素比较
int j = i;//记录循环到的位置的下标
int temp = arr[j];//保存遍历到的这个数,当从while循环了一次出来以后,temp就换了,换成了这一组的前一个
while (j - gap >= 0 && temp < arr[j - gap]) {//和本组的前一个比较,寻找正确的插入的位置
arr[j] = arr[j - gap];//相当于把元素后移
j -= gap;//当找到了位置以后,需要和前一个位置继续比较
}
}
}
}
五、快速排序:15ms
public static void quickSort(int[] arr, int left, int right) {
/*
步骤:
快速排序,就是找一个中轴点,左右各有一个下标向中间遍历,当左边的大于中轴点,右边的小于中轴点时,交换两个元素,如果两个下标
重合,表示本轮已经是左小右大,需要注意的是如果交换完以后发现arr[l] == pivot 的值,那么r要前移一位,同理如果交换完以后发现
arr[r] == pivot 的值,那么l要后移一位,此时一次循环完美结束。(顺序不能乱了,这里我也搞不太清楚,先记住吧)
递归时l和r必然相等,左右都要递归。需要注意的是如果l == r ,必须l++,r-- ,否则会栈溢出
*/
int l = left;//下标
int r = right;
int temp = 0;//临时变量,用于交换
//pivot 中轴
int pivot = arr[(left+right) / 2];//这里!!!!!!我本来写的arr[(arr.length-1)/2],,会报错!!!!!!离谱
//while循环是为了让比pivot小的放在左边,大的放到右边
while (l < r) {
while (arr[l] < pivot) {//在pivot左边一直找,找到比pivot要大的退出
l++;
}
while (arr[r] > pivot) {//在pivot右边一直找,找到比pivot要小的退出
r--;
}
if (l >= r) {//说明第一轮已经排好了,左边都小于pivot,右边都大于pivot
break;
}
//交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交换完以后发现arr[l] == pivot 的值,那么r要前移一位
if (arr[l] == pivot) {
r--;
}
//如果交换完以后发现arr[r] == pivot 的值,那么l要后移一位
if (arr[r] == pivot) {
l++;
}
}
//如果l == r ,必须l++,r-- ,否则会栈溢出
if (l == r) {
l++;
r--;
}
//向左递归
if (left < r) {
quickSort(arr, left, r);
}
//向右递归
if (l < right) {
quickSort(arr, l, right);
}
}
六、归并排序:13ms
归并排序是一个分+合的过程,为区分清楚各部分的功能,有两段代码
//合并的方法
/**
* @param arr 带排序的数组
* @param left 最左边的索引
* @param mid 中间的索引
* @param right 最右边的索引
* @param temp 中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;//左边初始索引
int j = mid + 1;//右边的初始索引
int t = 0;//temp的索引
//把两组有序的数据按规则放到temp中,直到一侧全放进去
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t++;
i++;
} else {
temp[t] = arr[j];
t++;
j++;
}
}
//把另一组数据全放进去
while (j<=right) {
temp[t] = arr[j];
t++;
j++;
}
while (i<=mid) {
temp[t] = arr[i];
t++;
i++;
}
//把temp中的数据再放回数组中,并不是每次都把所有的拷进去,只有最后一次是全拷进去了,前面的都不是,可能是2个,4个等等
t = 0;
int tempLeft = left;
//从头到尾都是一组下标,传入数据时,left为0,right为arr.length-1。所以设置临时左下标,右下标自己会变
//第一次tempLeft=0,right = 1
//第二次tempLeft =2,right = 3
//。。。
//最后一次tempLeft=0,right= 7
// System.out.println("tempLeft= " + tempLeft +",right="+right);
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
//分+合的方法
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if (left< right){
int mid = (left+right)/2;
//向左递归进行分解
mergeSort(arr,left,mid,temp);
//向右递归进行分解
mergeSort(arr,mid+1,right,temp);
//递归一次节合并一次
// System.out.println("********");
merge(arr,left,mid,right,temp);
}
}
七、基数排序(桶排序):31ms
public static void radixSort(int[] arr) {
//得到带排序数组中最大数
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
//得到最大值是几位数
int maxLength = (max + "").length();
//先造好十个桶,又因为每个桶中有元素,所以用二维数组
//考虑到每个桶要有arr.length的空间,不难发现基数排序是一个用空间换时间的方法
int[][] tong = new int[10][arr.length];
//声明一个一维数组表示每个桶有几个数
int[] num = new int[10];
// i表示第几个桶
// m为0表示看这一轮看个位,m为1表示看十位,依次类推
// n的出现方便取模,来获取想要看的位置的数字
// numIndex表示在个位、十位、百位...上的数字
// temp表示原来数组的下标,方便赋值
//元素放入相应的桶中
for (int m = 0, n = 1; m < maxLength; m++, n = n * 10) {
//第一次看个位,第二次看十位,第三次看百位。。。
for (int i = 0; i < arr.length; i++) {
int numIndex = arr[i] / n % 10;
tong[numIndex][num[numIndex]] = arr[i];
num[numIndex]++;
}
//元素取出来,放到原来的数组中
//需要每个桶都有指针来遍历
int temp = 0;
for (int i = 0; i < num.length; i++) {
if (num[i] != 0) {
for (int j = 0; j < num[i]; j++) {
arr[temp] = tong[i][j];
temp++;
}
}
num[i] = 0;//重置桶中的数据!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!很重要
}
// System.out.println(Arrays.toString(arr));
}
八、堆排序 :10ms
public static void heapSort(int[] arr) {
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
int temp;
for (int k = arr.length - 1; k > 0; k--) {
temp = arr[k];
arr[k] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, k);//每次都从0开始
}
System.out.println("升序数组:" + Arrays.toString(arr));
}
/**
* 将局部的树变成大顶堆
*
* @param arr 带排序数组
* @param i 非叶子节点的下标
* @param length 带排序数组的长度
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//直接挪到最左下的那个非叶子节点
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];//把大的数放在了堆顶(局部的)
i = k;//角标也挪上去,继续循环比较
} else {
break;
}
}
arr[i] = temp;//temp放到调整后的位置,完成交换
}