【Java数据结构与算法】简单排序、二分查找和异或运算
简单排序
选择排序
概念

首先,找到数组中最小的那个元素,其次,把它和数组的第一个元素交换位置(如果第一个元素就是最小的元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法叫做选择排序,因为它在不断地选择剩余元素中地最小者。
代码实现
public static void SelectionSort(int[] arr){
if(arr==null||arr.length<2) return; //去除多余情况
int N = arr.length;
for (int i = 0; i < N-1; i++) {
int minIndex = i;
for (int j = i+1; j < N; j++){
if(arr[j] < arr[minIndex]) minIndex = j; //更新每一轮最小元素的下标
}
swap(arr,i,minIndex);
}
}
public static void swap(int[] arr, int i, int j){ //交换元素
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
复杂度分析
选择排序过程中,0~N-1 上任意位置i都要进行一次交换和N-1-i次比较。因此总共有N次交换和(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2次比较。
也就是O(N^2)
冒泡排序
概念

从第一个元素开始遍历数组每两个元素一组比较,如果不满足从小到大的排序规则则进行交换,这样最后可以得到最大元素在数组最后的位置排好。如此往复,直到整个数组排好。
代码实现
public static void sort(int[] arr){
if(arr==null||arr.length<2) return; //去除多余情况
for(int i = arr.length - 1; i >= 0;i--){ //外层反向遍历
for(int j = 0;j < i; j++){
if(arr[j] > arr[j+1])
swap(arr,j,j+1);
}
}
}
public static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
复杂度分析
总的比较次数是(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2
也就是O(N^2)
插入排序
概念

从左向右遍历,每次把遍历到的元素放到前面已经排好顺序的数组的合适位置。如此往复,直到整个数组排好。
代码实现
public static void sort(int[] arr){
if(arr==null||arr.length<2) return; //去除无效情况
for(int i = 1; i < arr.length; i++){ //i从1开始,认为i为1时已经排好
for(int j = i-1; j >= 0 && arr[j] > arr[j+1]; j--)
swap(arr,j,j+1);
}
}
public static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
复杂度分析
总的比较和交换次数都是(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2
也就是O(N^2)
二分查找
左侧边界二分查找
在arr上,找满足>=value的最左位置
public static int nearestIndex(int[] arr, int value) {
int L = 0;
int R = arr.length - 1;
int index = -1;
while (L <= R) { //注意等号
int mid = L + ((R - L) >> 1);
if (arr[mid] >= value) { //注意
index = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
if(index == -1) return -1; //index可能为-1,防ArrayOutOfIndexException
else if(arr[index] == value)
return index;
else return -1;
}
右侧边界二分查找
在arr上,找满足>=value的最右位置
public static int nearestIndex(int[] arr, int value) {
int L = 0;
int R = arr.length - 1;
int index = -1;
while (L <= R) {
int mid = L + ((R - L) >> 1);
if (arr[mid] <= value) { //注意
index = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
if(index == -1) return -1; //index可能为-1,防止ArrayOutOfIndexException
else if(arr[index] == value)
return index;
else return -1;
}
总结
- 左侧边界二分查找
先写if (arr[mid] >= value) - 右侧边界二分查找
先写if (arr[mid] <= value) - 别忘了最后对index为-1做特殊处理,防止数组越界访问
异或运算
公式
底层表现在二进制上,异或运算使得相同数字得0,不同数字得1
1 ^ 1 = 0
0 ^ 0 = 0
1 ^ 0 = 1
| 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | |
| ^ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| = | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
0^N=N
N^N=0
满足交换律和结合律
应用
交换
a=a^b
b=a^b
a=a^b
实现了交换a,b的值
注意:前提是a,b有不同的内存空间。如果a,b是数组下标,那么当a=b的时候不能使用该方法,因为a,b代表数组一块相同的空间。
优点:位运算速度快,效率高
常考面试题
第一题
在一组数中,只有一种数出现了奇数次,其他类型的数出现了偶数次,问这个出现奇数次的数是什么,时间复杂度小于O(N)
解析:借助异或运算性质,N^N=0,只需要让所有数连续异或,最后的结果就是答案
public static void printOddTimesNum1(int[] arr) {
int eO = 0;
for (int cur : arr) {
eO ^= cur;
}
System.out.println(eO);
}
第二题
在一组数中,有两种数出现了奇数次,其他类型的数出现了偶数次,问这两个出现奇数次的数是什么,时间复杂度小于O(N)
解析:设这两个数是a和b,所以所有数字第一次连续异或可以得到a^b。下面我们只需要想办法搞出a或者b。
举个例子,我们成功搞出a来,那么异或运算aba就可以得到b。注意到a和b前提是肯定不相同,那么在二进制的层面看这两个数字,它们肯定至少有一位数字是不一样的,一个是0,一个是1
a:…………0001…………
b:…………0000…………
下面要做的就是把所有出现了两次的数字根据ab这一位不同的数字进行分组。
这一位数字为0的数字为一组,这一位数字为1的数字为一组

让其中一组进行连续异或,比如这一位为1的这一组数字连续异或,出现偶数次的数字异或为0,最后只剩下a,我们就成功得到了a。
最后进行a ^ b ^ a就可以得到b
本题默认寻找a,b从右到左第一次出现的不同二进制位的位置,这也是本题的关键,等价于求ab异或后从右向左第一次出现1的位置。
记住一个公式:原码和补码做与运算可以得到二进制数从右到左第一次出现1的位置为1,其他位置为0的数
| 1010111100 | |
| & | 0101000100 |
| = | 0000000100 |
public static void printOddTimesNum2(int[] arr) {
int eO = 0, eOhasOne = 0;
for (int curNum : arr) {
eO ^= curNum;
}
int rightOne = eO & (~eO + 1); 原码和补码做与运算
for (int cur : arr) {
if ((cur & rightOne) == 1) { 该位是1的为一组
eOhasOne ^= cur;
}
}
System.out.println(eOhasOne + " " + (eO ^ eOhasOne));
}
【Java数据结构与算法】简单排序、二分查找和异或运算的更多相关文章
- Java数据结构和算法 - 简单排序
Q: 冒泡排序? A: 1) 比较相邻的元素.如果第一个比第二个大,就交换它们两个; 2) 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数; 3) 针 ...
- Java数据结构和算法 - 高级排序
希尔排序 Q: 什么是希尔排序? A: 希尔排序因计算机科学家Donald L.Shell而得名,他在1959年发现了希尔排序算法. A: 希尔排序基于插入排序,但是增加了一个新的特性,大大地提高了插 ...
- golang数据结构和算法之BinarySearch二分查找法
基础语法差不多了, 就需要系统的撸一下数据结构和算法了. 没找到合适的书, 就参考github项目: https://github.com/floyernick/Data-Structures-and ...
- Java数据结构与算法之排序
排序从大体上来讲,做了两件事情: 1.比較两个数据项: 2.交换两个数据项.或复制当中一项 一.冒泡排序 大O表示法:交换次数和比較次数都为O(N*N). 算法原理: 1.比較相邻的元素.假设第一个比 ...
- java数据结构和算法------希尔排序
package iYou.neugle.sort; public class Shell_sort { public static void ShellSort(double[] array) { i ...
- java数据结构和算法------选择排序
package iYou.neugle.sort; public class Select_sort { public static void SelectSort(double[] array) { ...
- java数据结构和算法------合并排序
package iYou.neugle.sort; public class Merge_sort { public static void MergeSort(double[] array, i ...
- Java数据结构和算法(九)——高级排序
春晚好看吗?不存在的!!! 在Java数据结构和算法(三)——冒泡.选择.插入排序算法中我们介绍了三种简单的排序算法,它们的时间复杂度大O表示法都是O(N2),如果数据量少,我们还能忍受,但是数据量大 ...
- Java数据结构和算法(五)--希尔排序和快速排序
在前面复习了三个简单排序Java数据结构和算法(三)--三大排序--冒泡.选择.插入排序,属于算法的基础,但是效率是偏低的,所以现在 学习高级排序 插入排序存在的问题: 插入排序在逻辑把数据分为两部分 ...
随机推荐
- NVIDIA GPU的快速傅立叶变换
NVIDIA GPU的快速傅立叶变换 cuFFT库提供GPU加速的FFT实现,其执行速度比仅CPU的替代方案快10倍.cuFFT用于构建跨学科的商业和研究应用程序,例如深度学习,计算机视觉,计算物理, ...
- CPU的自动调度矩阵乘法
CPU的自动调度矩阵乘法 这是一个有关如何对CPU使用自动调度程序的文档. 与依靠手动模板定义搜索空间的基于模板的autotvm不同,自动调度程序不需要任何模板.用户只需要编写计算声明,而无需任何调度 ...
- 用NVIDIA NsightcComputeRoofline分析加速高性能HPC的应用
用NVIDIA NsightcComputeRoofline分析加速高性能HPC的应用 编写高性能的软件不是一件简单的任务.当有了可以编译和运行的代码之后,当您尝试并理解它在可用硬件上的执行情况时,将 ...
- Python爬虫入门:Urllib parse库使用详解(二)
文字转载:https://www.jianshu.com/p/e4a9e64082ef,转载内容仅供学习 如有侵权,请联系删除 获取url参数 urlparse 和 parse_qs ParseRes ...
- Git学习笔记(快速上手)
Git学习 1. 基本使用 安装成功后在开始菜单中会有Git项,菜单下有3个程序:任意文件夹下右键也可以看到对应的程序! Git Bash:Unix与Linux风格的命令行,使用最多,推荐最多 Git ...
- Java IO学习笔记四:Socket基础
作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...
- Air530Z GPS/北斗定位模块_设计指导手册_V1.2
下载PDF版本: Air530Z_定位模块_设计指导手册_V1.2.pdf @ 目录 1. 模块整体说明 2. 资料下载 3. 模块性能 4.模块管脚图 5.参考设计电路 6.GPS天线 6.1 无源 ...
- MindSpore自定义模型损失函数
技术背景 损失函数是机器学习中直接决定训练结果好坏的一个模块,该函数用于定义计算出来的结果或者是神经网络给出的推测结论与正确结果的偏差程度,偏差的越多,就表明对应的参数越差.而损失函数的另一个重要性在 ...
- Oracle几大后台进程
pmon,smon,dbwr,lgwr,reco,ckpt.六大核心进程.无论哪个进程出现故障,都会导致数据库实例崩溃.如果杀掉某个进程,在ALERT LOG中会发现各种错误. MMAN(Memory ...
- NUC980 运行 RT-Thread 时使用 GPIO
如何使用 GPIO? NuMaker-RTU-NUC980 板子引出的 IO 有: 分别有一个 I2C1.GPIO.SPI0.UART4,RT-Thread 中 NuMaker-RTU-NUC980 ...