核心思想: 通过相邻元素的比较和交换来将最大(或最小)的元素逐渐“冒泡”到数组的一端
具体步骤:
实现代码:
public static void bubbleSort(int[] arr) {
int temp = 0;
for (int i = arr.length - 1; i > 0; i--) {
// 每次需要排序的长度
for (int j = 0; j < i; j++) {
// 从第一个元素到第i个元素
if (arr[j] > arr[j + 1]) {
// 确保较大的元素排到后面
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
时间和空间复杂度:
优化: 增加标记符swap,用于确定当前一轮冒泡是否有元素进行交换,若没有则跳出循环,表示数组已经有序了
public static void bubbleSort(int[] arr) {
int temp = 0;
boolean swap;
for (int i = arr.length - 1; i > 0; i--) {
// 每次需要排序的长度
swap=false;
for (int j = 0; j < i; j++) {
// 从第一个元素到第i个元素
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swap=true;
}
}//loop j
if (swap==false){
//前面的一轮循环没有进行交换,数组已经有序
break;
}
}//loop i
}// method bubbleSort
核心思想: 在未排序部分的数据中选择最小(或最大)的元素,然后将其放置到已排序部分的末尾
具体步骤:
实现代码:
public static void selectionSort(int[] arr) {
int temp, min = 0;
for (int i = 0; i < arr.length - 1; i++) {
min = i;
// 循环查找最小值
for (int j = i + 1; j < arr.length; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (min != i) {
temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
}
时间和空间复杂度:
关于稳定性的探讨:
稳定性:在排序过程中,相等元素的相对顺序是否会被改变。如果排序算法能够保持相等元素的相对顺序不变,则称其为稳定的排序算法;反之,则称其为不稳定的排序算法
冒泡排序(升序):当相邻元素进行比较并需要交换位置时,只有当后面的元素小于前面的元素才会进行交换。因此,对于相等的元素,即使它们相邻,它们的相对顺序也不会被改变,从而保持了排序的稳定性
选择排序:每次选择最小的元素并放置到已排序部分的末尾。在寻找最小(或最大)元素的过程中,可能会导致相等元素的相对顺序发生改变。例如,在一组相等的元素中,由于选择排序每次只选择一个最小(或最大)元素,所以在选择的过程中可能会交换这些相等元素的位置,从而导致排序的不稳定性
举例:假设我们有以下一组待排序的元素:
[5①, 2, 7, 5②, 1]
冒泡排序:比较相邻的元素,并根据需要交换它们的位置。当遇到相等元素时,只有在后面的元素小于前面的元素时才会进行交换。
第一轮冒泡排序:[2, 5①, 5②, 1, 7]
第二轮冒泡排序:[2, 5, 1, 5, 7]
第三轮冒泡排序:[2, 1, 5, 5, 7]
第四轮冒泡排序:[1, 2, 5, 5, 7]
选择排序:
第一轮选择排序:[1, 2, 7, 5②, 5①](选择1) (两个相等的5的位置发生变化,不稳定!!!)
第二轮选择排序:[1, 2, 5, 5, 7](选择2)
第三轮选择排序:[1, 2, 5, 5, 7](选择5)
第四轮选择排序:[1, 2, 5, 5, 7](选择5)
第五轮选择排序:[1, 2, 5, 5, 7](选择7)
核心思想: 将待排序的元素逐个插入到已排序部分的正确位置
具体步骤:
实现代码:
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
//从第二个元素开始,第一个元素默认有序
int key = arr[i]; //保存当前元素 key
int j = i - 1;
//找到合适的位置
while (j >= 0 && arr[j] > key) {
//发现已排序元素比 key 大,则将该元素向后移动一位,直到找到 key 的正确位置
arr[j + 1] = arr[j];
j = j - 1; //往后移
}
arr[j + 1] = key;1 //找到 key 保存的位置
}
}
待排序数组:[5, 2, 4, 6, 1, 3]
1:[2, 5, 4, 6, 1, 3]
2:[2, 4, 5, 6, 1, 3]
3:[1, 2, 4, 5, 6, 3]
4:[1, 2, 3, 4, 5, 6]
时间和空间复杂度:
核心思想:利用二分查找来确定待插入元素在已排序部分中的位置,以减少比较次数
实现步骤:
实现代码:
public static void binaryInsertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int left = 0;
int right = i - 1;
// 二分查找确定插入位置
int insertIndex = binarySearch(arr, left, right, key);
// 将大于 key 的元素向后移动一位
for (int j = i - 1; j >= insertIndex; j--) {
arr[j + 1] = arr[j];
}
// 插入 key
arr[insertIndex] = key;
}
}
// 二分查找
private static int binarySearch(int[] arr, int left, int right, int key) {
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) {
return mid; // key 在已排序部分的位置
} else if (arr[mid] < key) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left; // 返回待插入位置
}
时间和空间复杂度:
核心思想: 采用分治法,将已有序的子序列合并,得到完全有序的序列;
步骤:
实现代码:
public static void mergeSort(int[] arr){
int[] temp =new int[arr.length];
internalMergeSort(arr, temp, 0, arr.length-1);
}
private static void internalMergeSort(int[] arr, int[] temp, int left, int right){
//当left==right的时,已经不需要再划分了
if (left<right){
int middle = (left+right)/2;
internalMergeSort(arr, temp, left, middle); //左子数组
internalMergeSort(arr, temp, middle+1, right); //右子数组
mergeSortedArray(arr, temp, left, middle, right); //合并两个子数组
}
}
// 合并两个有序子序列
private static void mergeSortedArray(int arr[], int temp[], int left, int middle, int right){
int i=left;
int j=middle+1;
int k=0;
while (i<=middle && j<=right){
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while (i <=middle){
temp[k++] = arr[i++];
}
while ( j<=right){
temp[k++] = arr[j++];
}
//把数据复制回原数组
for (i=0; i<k; ++i){
arr[left+i] = temp[i];
}
}
时间和空间复杂度:
适用场景: 归并排序在数据量比较大的时候也有较为出色的表现(效率上),但是,其空间复杂度O(n)使得在数据量特别大的时候(例如,1千万数据)几乎不可接受。而且,考虑到有的机器内存本身就比较小,因此,采用归并排序一定要注意。
核心思想: 通过选取基准元素,将数组划分为两个子数组,并对子数组递归排序,实现整个数组的快速排序
实现步骤:
实现代码:
public static void quickSort(int[] arr){
qsort(arr, 0, arr.length-1);
}
private static void qsort(int[] arr, int low, int high){
if (low >= high)
return;
int pivot = partition(arr, low, high); //将数组分为两部分
qsort(arr, low, pivot-1); //递归排序左子数组
qsort(arr, pivot+1, high); //递归排序右子数组
}
private static int partition(int[] arr, int low, int high){
int pivot = arr[low]; //基准
while (low < high){
while (low < high && arr[high] >= pivot){
--high;
}
arr[low]=arr[high]; //比基准小的元素会被移动到基准的左边
while (low < high && arr[low] <= pivot){
++low;
}
arr[high] = arr[low]; //比基准大的元素会被移动到基准的右边
}
//扫描完成,基准到位
arr[low] = pivot;
//返回的是基准的位置
return low;
}
举例:
初始:[7, 2, 1, 6, 8, 5, 3, 4] # 7为基准
第一:[4, 2, 1, 6, 8, 5, 3, 4] # 4移到左边
[4, 2, 1, 6, 8, 5, 3, 8] # 8移到右边
[4, 2, 1, 6, 3, 5, 3, 8] # 3移到左边
[4, 2, 1, 6, 3, 5, 7, 8] # 基准7插入到low位置
划分为两个数组:[4, 2, 1, 6, 3, 5]和[8]
第二: [4, 2, 1, 6, 3, 5] # 以4为基准
[3, 2, 1, 6, 3, 5] # 3移到左边
[3, 2, 1, 6, 6, 5] # 6移到右边
[3, 2, 1, 4, 6, 5] # 基准4插入到low位置
以此类推......
时间和空间复杂度:
核心思想: 通过较大间隔的插入排序来使数组中的元素局部有序,从而减少后续插入排序的工作量
实现步骤:
实现代码:
public class ShellSort {
public static void shellSort(int[] arr) {
int n = arr.length;
// 初始化增量,这里使用希尔增量序列
int gap = 1;
while (gap < n / 3) {
gap = gap * 3 + 1;
}
// 增量逐步缩小至1
while (gap > 0) {
// 对每个子序列进行插入排序
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j = i;
// 对当前子序列进行插入排序
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
// 缩小增量
gap = gap / 3;
}
}
public static void main(String[] args) {
int[] arr = {
12, 34, 54, 2, 3};
shellSort(arr);
System.out.println("排序后的数组:");
for (int num : arr) {
System.out.print(num + " ");
}
}
}
时间和空间复杂度:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
假设给定无序序列结构如下:
此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。
找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6
此时,我们就将一个无需序列构造成了一个大顶堆。
将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
将堆顶元素9和末尾元素4进行交换:
重新调整结构,使其继续满足堆定义:
再将堆顶元素8与末尾元素5进行交换,得到第二大元素8:
后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
import java.util.Arrays;
public class Main{
public static void main(String[] args) {
int[] arr = new int[]{
4,6,8,5,9};
int length = arr.length;
//从最后一个非叶节点开始构建大顶堆
for (int i = arr.length/2-1; i >=0; i--) {
maximumHeap(i,arr,length);
}
//从最小的叶子节点开始与根节点进行交换并重新构建大顶堆
for (int i = arr.length-1; i >=0; i--) {
// System.out.println(Arrays.toString(arr));
swap(arr,0,i);
length--;
maximumHeap(0,arr,length);
}
System.out.println(Arrays.toString(arr));
}
//构建大顶堆
public static void maximumHeap(int i,int[] arr,int length){
int temp = arr[i];
for (int j = i*2+1; j < length; j=j*2+1) {
//如果右孩子大于做孩子,则指向右孩子
if(j+1<length && arr[j+1]>arr[j]){
j++;
}
//如果最大的孩子大于当前节点,则将大孩子赋给当前节点,修改当前节点为其大孩子节点,再向下走。
if(arr[j]>temp){
arr[i] = arr[j];
i = j;
}else{
break;
}
}
//将temp放到最终位置
arr[i] = temp;
}
//交换
public static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
1. 时间复杂度: 堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度最好和最坏情况下都是**O(nlogn)**级。
2. 空间复杂度:堆排序不要任何辅助数组,只需要一个辅助变量,所占空间是常数与n无关,所以空间复杂度为O(1)。
排序 | 排序方法 | 平均情况 | 最好情况 | 最坏情况 | 空间 | 稳定性 |
---|---|---|---|---|---|---|
1 | 冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
2 | 选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
3 | 插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
4 | 二分插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
5 | 归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
6 | 快速排序 | O(nlogn) | O(nlogn) | O(n2) | O(logn)~O(n) | 不稳定 |
7 | 希尔排序 | O(nlogn) ~ O(n2) | O(n1.3) | O(n2) | O(1) | 不稳定 |
8 | 堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland