排序
排序
交换(swap)
异或
异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法(0+1+1,1+1等于1)。
异或略称为XOR、EX-OR
性质
- 0 ^ N = N,任何一个数与0异或等于自身。
- N ^ N = 0,任何一个数异或本身等于0。
- 异或运算满足交换律和结合律。a ^ b = b ^ a,a ^ b ^ c = a (b ^ c)
- 由3推出本结论
利用异或交换两个值(排序中的交换可以这样写)
1 | int a = 1, b = 2; |
以上操作能交换两个数,不需要申请额外的空间, 前提a和b必须是内存是不同的。
查找数组中出现奇数次的数可用异或查找。
查找一个出现奇数的数
int eor = 0;
用eor分别异或数组中的每个数,最终eor的值就是出现奇数次的数字。利用异或的性质,任意相同的数异或为0,偶数次则异或为0, 0异或一个数为本身。
查找两个出现奇数的数
int eor = 0;
用eor分别异或数组中的每个数,最终eor的值为
eor = a ^ b
(a,b为出现奇数次数的数字)。所以这样就拿到了a,b的异或值eor。a!=b,所以eor != 0。这里做个假设:假设eor中的第八位为1,则可按第八位为0还是1将数组中的数分成两类,再新建一个变量
int eor1 = 0;
用这个分别去异或数组中第八位为1的数,最终结果为eor1 = a or b;
因为出现偶数次的数都会被抹掉,所以最终要么留下a要么留下b。好难解释,我要是不理解的话我也看不懂我写的。
代码实现:
1 | public static void printOddTimesNum2(int [] arr){ |
选择排序
对于要排序的数组,设置一个minIdx记录最小数字下标。先假设第1个数字最小,此时minIdx = 0,将arr[minIdx]
与后续数字逐一比较,当遇到更小的数字时,使minidx等于该数字下标,第一轮比较找出该数组中最小的数字。找到后将minIdx下标的数字与第1个数字交换,此时称一个数字已被排序。然后开始第2轮比较,令minIdx = 1,重复上述过程。每一轮的比较将使得当前未排序数字中的最小者被排序,未排序数字总数减1。第arr.length - 1
轮结束后排序完成。每次找最小的。
找到一个动图,动图好理解:
![select](https://cos.asuka-xun.cc/blog/background picture1652692082-UXzLDc-select.gif)
复杂度分析
时间复杂度:两层for循环,第一轮比较n-1
次,最后一轮比较1次,等差数列,总比较次数 n * (n - 1) / 2
次,时间复杂度为O(n^2)。
空间复杂度:常数项变量,O(1)。
代码块:
1 | public static void selectionSort(int[] arr) { |
冒泡排序
逐个进行比对,每一轮比对都将使未排序数字总数减1。从第一位开始比较两个数字,若前者大,则交换两者位置,比较位往后移。也就是说比较arr[0]
和arr[1]
,如果arr[0]
>arr[1]
,交换两者位置,比较位移到arr[1]
和arr[2]
这两个数字,直到比较到arr[n-2]
和arr[n-1]
(n=arr.length
)。这一轮结束,最大的数字被放到后面,下一轮比较总数减一。相邻两个数两两比较,小的放前面,比较一轮后大的在最后面。
复杂度分析
时间复杂度:每轮交换总数都减一,等差数列。第一轮比较N-1次,依次递减。所以前n项和为aN^2+bN+c
所以时间复杂度为O(N^2);
空间复杂度:算法中只有常数项变量,O(1)。
代码块:
1 | public static void bubbleSort(int[] arr) { |
插入排序
对于待排序数组,从第2个元素开始(称作插入对象元素)
,比较它与之前的元素(称作比较对象元素),当插入对象元素小于比较对象元素时,继续往前比较,直到不小于(≥)比较对象,此时将插入对象元素插入到该次比较对象元素之后。重复这个插入过程直到最后一个元素作为插入对象元素完成插入操作。遍历数组,每个数都去比较,找到小的数字合适的位置插入进去。
动图:
复杂度分析
时间复杂度:两层for循环,第一层N-1轮,逐次减少比较。所以也是等差数列。时间复杂度为O(N^2)。
空间复杂度:算法中只有常数项变量。
代码块:
1 | public static void insertionSort(int [] arr) { |
归并排序
利用递归进行排序。首先将数组分成左右两部分,分别进行排序。最终两边都排序完成,然后两边分别设置一个指针,然后两个指针比较,谁小谁就拿下来,然后这边的指针往后移动。相等时可以取左也取右,自己规定。指针移动直到有一边越界。
1 | public static int[] mergeSort(int[] arr) { |
复杂度分析
符合master公式,则由性质知时间复杂度
$$
O({N}^{d} * \log_2N)
$$
其中d为1。
额外空间复杂度:建立了一个辅助数组所以为O(N)。
Author: xun
Link: http://blog.fooo.in/2022/06/27/algorithm/sort/
License:
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。