1. 认识复杂度和简单排序算法。
第1节、时间复杂度
先从选择排序看起
选择排序:从第1位到第N位。每一位分别与之后的所有数比较。比较小的树放在第i位。
计算时间复杂度:
每一个数分别看了后面的数:N次、N-1次、N-2次···1次。
每个数分别比较了后面的数:N次、N-1次、N-2次···1次。
每个数都交换了一次:1次。
所以可以得出时间复杂度:
每个数看了: N+N-1+N-2+···+1。
每一个数比较了: N+N-1+N-2+···+1。
总共交换了N次。
全部相加可以得到: (N+1)*N + N 。
它是一个: 最高次项为二次的方程 aN^2+bN+c。
得到时间复复杂度为 O(N^2) 读作:big o N^2。
第2节、额外空间复杂度(选择排序)
这里我们也可以从选择排序看起
下面我们给出选择排序的代码:
namespace LeetCode_Learn
{
public class Part01_SelectionSort
{
public static int[] Sort(int[] arr)
{
if (arr == null)
throw new ArgumentNullException("arr不可为空!");
if (arr.Length < 2)
return arr;
for (int i = 0; i < arr.Length; i++)
{
int minIndex = i;
for (int j = i + 1; j < arr.Length; j++)
{
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
Swap(arr, i, minIndex);
}
return arr;
}
private static void Swap(int[] arr, int index1, int index2)
{
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
}
在遍历每个数字的时候,我们会声明一个temp作为最小的最小的index。每次去每次循环完之后会释放掉这个temp,所以这里我们只用到了开辟了一个变量。也就是额外空间复杂度为O(1) 。
第3节、冒泡排序
冒泡排序:遍历每个数,其左边边的数做以下操作: 如果右边的数大于左边的数则交换。
下面给出代码:
namespace LeetCode_Learn
{
public class Part02_BubbleSort
{
public static int[] Sort(int[] arr)
{
if (arr == null)
throw new ArgumentNullException("arr不可为空!");
if (arr.Length < 2)
return arr;
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);
}
}
}
return arr;
}
private static void Swap(int[] arr, int index1, int index2)
{
arr[index1] = arr[index1] ^ arr[index2];
arr[index2] = arr[index1] ^ arr[index2];
arr[index1] = arr[index1] ^ arr[index2];
}
}
}
这里用到异或运算进行。进行交换操作。
第4节、异或运算
下面给出异或运算的一些规律:
-
0^N = N。
-
N^N = 0。
-
符合交换律和结合律。
-
异或运算与顺序无关,可以调换顺序。
-
注意事项:如果要进行用异或运算进行交换操作,请注意a和b指向的内存空间不能为同一个。因为N^N=0。
下面给出一道异或运算的练习题:
给出一个整形数组 int[] arr
第1问:这个数组中仅有一种数出现奇数次,其他数都是偶数次。求这个基数是多少?
第2问:这个数组中仅有二种数出现奇数次。其他数都是都是偶数次。求这两个奇数分别是多少?
第1题,题解:
定义eor = 0,用eor 异或每一个数得到的数一定是出现奇数次的数。因为出现偶数次的数异或为0。
namespace LeetCode_Learn
{
public class Part03_GetOneOddNumber
{
public static int Get(int[] arr)
{
if (arr == null)
throw new ArgumentNullException("arr不可为空!");
if (arr.Length < 2)
return arr[0];
int eor = 0;
for (int i = 0; i < arr.Length; i++)
{
eor ^= arr[i];
}
return eor;
}
}
}
第2题,题解:
同上题用eor异或每一个数。得到的eor为 a异或b。 a和b是那两个出现奇数次的数。然后我们在定义一个变量_eor = 0。因为a和b不为同一个数,所以 eor不等于0。所以我们一定可以在eor中找到一位为1。这里我们用一个算法来找到最右侧的1:
算法为 rightOne = eor & (~eor+1) 。
a和b在rightOne这一位,一定有一位是1,有一位是0。这里我们将数组分为两种类型。种是对应位为1,一种是对应位为0。a和b肯定只占一种类型。然后我们遍历对应位为1的数。用_eor异或这些数。我们一定可以得到a或者b。然后我们再用得到的这个数,来异或eor。这样我们就得到了这两个数。
namespace LeetCode_Learn
{
public class Part04_GetTwoOddNumber
{
public static int[] Get(int[] arr)
{
if (arr == null)
throw new ArgumentNullException("arr不可为空!");
if (arr.Length < 2)
return arr;
int eor = 0;
for (int i = 0; i < arr.Length; i++)
{
eor ^= arr[i];
}
int _eor = 0;
int rightOne = eor & (~eor + 1);
for (int i = 0; i < arr.Length; i++)
{
if ((arr[i] & rightOne) == 0)
{
_eor ^= arr[i];
}
}
int a = eor ^ _eor;
int b = eor ^ a;
return new int[] { a, b };
}
}
}