笔记 左程云算法基础

01 认识复杂度和简单排序算法

 #时间复杂度

常数操作举例

属于常数操作:int a = arr[i];数组中,只是算了一个偏移量;加减乘除;位运算...

不属于常数操作:int b = list.get(i);链表中,只能遍历去找

当两个算法时间复杂度相等时,只能实际去运行来确定哪个算法更优

#选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。

过程:看N眼,N次比较,1次swap;看N-1眼,N-1次比较,1次swap.......

看:N+N-1+N-2+...  比较:N+N-1+N-2+...   swap:N次

=aN^2+bN+c

不要低阶项,只要高阶项,不要常系数==》O(n^2)

只需要开辟几个变量的额外空间==》O(1)

public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length - 1; i++) {//i~N-1
			int minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {//i~N-1上找最小值下标
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			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;
	}
void swap(int arr[], int i, int j) {
	int tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

void selectionSort(int arr[],int len) {
	if (arr[0] == NULL || len < 2) {
		return;
	}
	for (int i = 0; i < len - 1; i++) {//确定范围为 i~N-1
		int minIndex = i;
		for (int j = i + 1; j < len; j++) {//i~N-1上找最小值下标
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
		}
		swap(arr, i, minIndex);
	}
}

#冒泡排序

重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int e = arr.length - 1; e > 0; e--) {//规定范围 0~e
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

    //交换arr的i和j位置上的值
	public static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
void swap(int arr[], int i, int j) {
	arr[i] = arr[i] ^ arr[j];
	arr[j] = arr[i] ^ arr[j];
	arr[i] = arr[i] ^ arr[j];
}

void bubbleSort(int arr[],int len) {
	if (arr == NULL || len < 2) {
		return;
	}
	for (int e = len - 1; e > 0; e--) {
		for (int i = 0; i < e; i++) {
			if (arr[i] > arr[i + 1]) {
				swap(arr, i, i + 1);
			}
		}
	}
}

#异或运算 ^

相同为0,不同为1:0^0=0        0^1=1        1^0=1        1^1=0

可以理解为无进位相加

  • 0^N=0         N^N=0
  • 满足交换律和结合律

//交换a和b的值(不用额外变量)

a=a^b;
b=a^b;
a=a^b;

但是前提是:a和b的内存地址不同,地址相同时会被洗成0

题目-数组中找奇数次出现的数

1 在一个数组中,只有一个数出现了奇数次,其他数出现偶数次,怎么找出这个数?

int eor =0; 把eor从数组头异或到数组尾,结束时eor就是出现了奇数次的数(偶次彼此消掉了)

public static void printOddTimesNum1(int[] arr) {
		int eor = 0;
		for (int cur : arr) {
			eor ^= cur;
		}
		System.out.println(eor);
	}
void printOddTimesNum1(int arr[],int len) {
	int eor = 0;
	for (int i = 0; i < len;i++) {
		eor ^= arr[i];
	}
	cout<<"数组中次数为奇数的数为:"<<eor;
}

异或运算的顺序无所谓的原因:因为异或运算可以看成无进位相加,结果中某一位的值只和所有数的这一位1出现的次数有关,和顺序无关

2 在一个数组中,两种数出现了奇数次,其他数出现偶数次,怎么找出这个数?

int eor =0;设出现奇数次数为a和b,把eor从数组头异或到数组尾,结束时eor就是a^b

所以eor=a^b     (a!=b   ——》eor!=0)

eor一定在某一位(至少一位)不等于0 ,假设第X位为1,说明a和b在第X位不一样

int eor'=0;把eor’从数组头异或到数组尾,只异或数组中那些X位不为0的数,结束时eor'就是a或者b

 X位0的数不影响结果,只异或数组中那些X位不为0的数时,other2中1的个数为偶数次全消除,只剩a或b中一个,eor'只能碰到a或者b中一个,得到的结果就是另外一个

eor^eor'是a或b的另外一个

public static void printOddTimesNum2(int[] arr) {
		int eor = 0;
		for (int i=0;i<arr.length;i++) {
			eor^= arr[i];
		}
//eor=a^b
//eor!=0
//eor必然有一个位置是1

		int rightOne = eor & (~eor + 1);//提取出最右边的1

        int eorhasone=0;//eor'
		for (int cur : arr) {
			if ((cur & rightOne) != 0) {
				eorhasone ^= cur;
			}
		}
		System.out.println(eorhasone + " " + (eor ^ eorhasone));
	}
//提取最右边的1 
int rightOne = eor & (~eor + 1);

eor:         010100
~eor:        101011
~eor+1:      101100
eor&~eor+1:  000100
void printOddTimesNum2(int arr[],int len) {
	int eor = 0;
	for (int i = 0; i < len; i++) {
		eor ^= arr[i];
	}
	//eor=a^b
	//eor!=0
	//eor必然有一个位置是1

	int rightOne = eor & (~eor + 1);//提取出最右边的1

	int eorhasone = 0;//eor'
	for (int i = 0; i < len; i++) {
		if ((arr[i] & rightOne) != 0) {
			eorhasone ^= arr[i];
		}
	}
	int eor2 = eor ^ eorhasone;
	cout << "数组中出现奇数次数为:" << eorhasone << "和" << eor2;
}

#插入排序

对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增 1 的有序表 。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

插入排序的时间复杂度和数据状况有关

选择排序和冒泡排序和数据状况无关,不影响流程的进行

估计算法时间复杂度时,一律按照算法可能遇到的最差情况估计

public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
//0-0已经有序
//将0-i做到有序
		for (int i = 1; i < arr.length; i++) { //0-i范围
			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) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
void swap(int arr[], int i, int j) {
	arr[i] = arr[i] ^ arr[j];
	arr[j] = arr[i] ^ arr[j];
	arr[i] = arr[i] ^ arr[j];
}

void insertionSort(int arr[],int len) {
	if (arr == NULL || len < 2) {
		return;
	}
	//0-0已经有序
	//将0-i做到有序
	for (int i = 1; i < len; i++) { //范围
		for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
			swap(arr, j, j + 1);
		}
	}
}

#二分法

题目-局部最小问题

arr数组中,无序,任何两个相邻数一定不相等;找一个局部最小的位置,能否时间复杂度好于O(n)?(局部最小定义:位置为0的数<位置为1的数 或 N-2<N-1 或者  i-1<i<i+1)

二分不一定需要有序

优化一个流程,优化的方向:1.数据状况特殊 2.问题特殊

#对数器

	// 和系统排序方法做对比
	public static void comparator(int[] arr) {
		Arrays.sort(arr);
	}

	//生成随机数组
	public static int[] generateRandomArray(int maxSize, int maxValue) {
    //Math.random()          [0,1)所有小数,等概率返回一个
    //Math.random()*N        [0,N)所有小数,等概率返回一个
    //(int)Math.random()*N   [0,N-1]所有整数,等概率返回一个
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];//长度随机
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());//每个值随机
		}
		return arr;
	}

	// for test
	public static int[] copy
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值