数据结构与算法学习-1(左程云,c++)

时间复杂度与简单排序算法

时间复杂度 O

  1. 一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。
  2. 时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。具体来说,先要对一个算法流程非常熟悉,然后去写出这个算法流程中,发生了多少常数操作, 进而总结出常数操作数量的表达式。
  3. 在表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果为f(N),那么时间复杂度为O(f(N))。
  4. 评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行 时间,也就是“常数项时间”。

补充说明:

  1. 时间复杂度的O表示一个最坏情况,算法的上限
  2. 两个同时间复杂度的算法进行比较要实际测试
  3. O(log2^N)默认为以2为底

异或(^)

异或(^)可以理解为无进位相加(可解释异或的交换性质)。

0^1=1

1^1=0

0^0=0

运算性质与拓展

  1. 0 ^ N = N       N ^ N = 0
  2. 异或运算满足交换律和结合律:异或的顺序不影响结果

        a = 1^5^2^3^5^5^3^2^2^1^3^4

           = 1^1^2^2^2^3^3^3^4^5^5^5

           = 2^3^4^5

应用

第 1 题:数值交换

不用额外变量交换两个数

void swap(int& a,int& b){
    a=a^b;      // a = a^b  		   b = b
    b=a^b;      // a = a^b   		   b = (a^b)^b
    a=a^b;      // a = (a^b)^(a^b^b)   b = (a^b)^b   
}

注意:用异或运算可以省空间(无需中间变量),但要求两个变量在不同的内存空间里,否则会变成零

第 2 题:

题目 :一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数?

思路分析:利用异或运算,计算性质,N ^ N=0,交换律结合律,偶数次的数字最终会全部消掉,剩下奇数次的数

void printOddTimesNum(vector<int> array) {
	int arrLen = array.size();
	int eor = 0;
	for (int i = 0; i < arrLen; i++) {
		eor ^= array[i];
	}
	cout << eor << endl;
}

第 3 题:

题目 :一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两个数

思路分析:由于只有两个数出现了奇数次,不妨设出现奇数次的两个数分别为 a , b

则由题意可得,所有数 异或起来可得 eor = a ^ b 且不等于0 ,则必存在a,b(二进制)中至少有一位不相等,

假设 a,b 中第6位(最右边)不同,且 a 中第6位为1,b 中第6位为0,则 eor 的 第6位(最右边)为1,

提取 eor 最右边的 1 (二进制数) 为 rightOne (0000 0100),

然后根据第6位是否为 1 (rightOne),可以将给定数组分成两堆,第6位为1的(含a堆)和第6位为0的(含b堆) ,

将含a堆的所有数异或,偶数次的数 异或 依然为0,因此得到其中一个奇数 onlyOne = a

另一个奇数 otherOne = eor ^ onlyOne = (a ^ b) ^ a = b

void printOddTimesNum(vector<int>& array) {
	// 假设数目为奇数的两种,分别为啊 a ,b
	int arrLen = array.size();
	int eor = 0;
	for (int i = 0; i < arrLen; i++) {
		eor ^= array[i];
	}
	// eor = a ^ b;
	int rightOne = eor & (~eor + 1); // 提取eor最右边的1
	int onlyOne = 0;
	for (int i = 0; i < arrLen; i++) {
		if ((array[i] & rightOne) != 0) {// 将数组分成两堆
			onlyOne ^= array[i];
		}
	}
	// onlyOne = a | b;
	int otherOne = eor ^ onlyOne;
	cout << onlyOne << "\t" << otherOne << endl;
}

提取一个数最右边的1 : res = n &(~n+1);

例:0100 0110(70) ----> 0000 0010(2)

int n = 70;

int res = n &(~n+1); // res = 2

 

二分法

第 1 题:

题目:在一个有序数组中找一个数是否存在。

bool isExist(vector<int>& array,int target){
	int arrLen = array.size();
    int L = 0;
    int R = arrLen - 1;
    while(L <= R){
        int mid = L + ((R -L) >> 1);
        if(array[mid] == target){
            return true;
        }
		if(array[mid] > target){
            R = mid - 1;
        }else{
            L = mid + 1;
        }
    }
    return false;
}

第 2 题:

题目:在一个有序数组中,找>=某个数最左侧的位置。(或者找<=某个数最右侧的位置)

int findLeftNum(vector<int>& array, int target) {

	int index = -1;
	int arrLen = array.size();
	int L = 0;
	int R = arrLen - 1;
	while (L <= R) {
		int mid = L + ((R - L) >> 1);
		if (array[mid] >= target) {
			index = mid;
			R = mid - 1;
		}
		else {
			L = mid + 1;
		}
	}
	return index;
}

int findRightNum(vector<int>& array, int target) {
	int index = -1;
	int arrLen = array.size();
	int L = 0;
	int R = arrLen - 1;
	while (L <= R) {
		int mid = L + ((R - L) >> 1);
		if (array[mid] <= target) {
			index = mid;
			L = mid + 1;
		}
		else {
			R = mid - 1;
		}
	}
	return index;
}

第 3 题:

题目:局部最小值问题。(数组A无序,相邻数一定不相等,找一个局部最小值)

int findMinNum(vector<int>& array) {
	int arrLen = array.size();
	int L = 0;
	int R = arrLen - 1;
	if (array[0] < array[1]) {
		return 0;
	}
	if (array[R] < array[R - 1]) {
		return R;
	}
	// 前面都不满足则一定存在局部最小值在中间
	while (L <= R) {
		// 先判断首位元素是否满足要求 
		int mid = L + ((R - L) >> 1);
		if (array[mid] < array[mid - 1] && array[mid] < array[mid + 1]) {
			return mid;
		}
        // 这里位置替换和普通二分法不一样
    	// 不可以用  R = mid - 1;L = mid + 1; 来更新边界位置,会发生越界
        // 例如 数组大小为6的时候  {1, -5, 2, 10, 6, -3}
        // L=0,R=5 -> mid=2  更新后 L=0,R=1 -> mid=0
        // 存在条件判断 array[mid - 1] 就会越界
		else if (array[mid] > array[mid - 1]) {
			R = mid;
		}
		else {
			L = mid;
		}
	}
	return -1;
}

对数器

  1. 有一个你想要测的方法a
  2. 实现复杂度不好但是容易实现的方法b
  3. 实现一个随机样本产生器
  4. 把方法a和方法b跑相同的随机样本,看看得到的结果是否一样。
  5. 如果有一个随机样本使得比对结果不一致,打印样本进行人工干预,改对方法a或者方法b
  6. 当样本数量很多时比对测试依然正确,可以确定方法a已经正确

随机Vector<int>数组生成

头文件 RandomArray.h

#pragma once
#include <iostream>
using namespace std;

#include <vector>


class RandomArray {
public:
	//声明静态成员函数
	static void generateRandomArray(vector<int>& randomArray, int maxSize, int maxValue);
	static void printArrayInfo(vector<int> array);
};

源文件 RandomArray.cpp

#include "RandomArray.h"

void RandomArray::generateRandomArray(vector<int>& randomArray, int maxSize, int maxValue) {
	if (!randomArray.empty()) {
		randomArray.clear();
	}
	int randomSize = rand() % (maxSize + 1) + 1; // 生成 1~maxSize 之间的一个随机整数
	randomArray.resize(randomSize);
	for (int i = 0; i < randomSize; i++) {
		int randomValue = (rand() % (maxValue + 1)+1) - (rand() % (maxValue + 1));
		randomArray[i] = randomValue;
	}
}


void RandomArray::printArrayInfo(vector<int> array) {
	int arraySize = array.size();
	for (int i = 0; i < arraySize; i++) {
		cout << array[i] << " ";
	}
	cout << endl;
}

排序算法

选择排序

时间复杂度O(n2),额外空间复杂度O(1)

  • 0 - n-1 选择一个最小的排在0号位置
  • 1 - n-1选择一个最小的排在1号位置
  • 2 - n -1选择一个最小的排在2号位置
    ...
    以此类推,代码如下:
// 选择排序不适合使用 异或 来互换值
// array[i], array[minIndex] 有可能指向同一个内存空间 , 异或结果会为0
void swap(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void selectionSort(vector<int>& array) {
	for (int i = 0; i < array.size() - 1; i++) {
		int minIndex = i;
		for (int j = i + 1; j < array.size(); j++) {
			minIndex = array[j] < array[minIndex] ? j : minIndex;
		}
		swap(array[i], array[minIndex]);
	}
}

冒泡排序

时间复杂度O(n2) 空间复杂度O(1) 稳定

void swap(int& a, int& b) {
	a = a ^ b;      // a = a^b  		   b = b
	b = a ^ b;      // a = a^b   		   b = (a^b)^b
	a = a ^ b;      // a = (a^b)^(a^b^b)   b = (a^b)^b   
}

void bubbleSort(vector<int>& array) {
	for (int i = 0; i < array.size() - 1; i++){
		for (int j = 0; j < array.size() - 1 - i; j++){
			if (array[j] > array[j + 1]){
				swap(array[j], array[j + 1]);
			}
		}
	}
}

插入排序

时间复杂度O(n2) 空间复杂度O(1) 稳定

void swap(int& a, int& b) {
	a = a ^ b;      // a = a^b  		   b = b
	b = a ^ b;      // a = a^b   		   b = (a^b)^b
	a = a ^ b;      // a = (a^b)^(a^b^b)   b = (a^b)^b   
}

void insertSort(vector<int>& array) {
	for (int i = 1; i < array.size(); ++i){
		for (int j = i-1; j >= 0 && array[j] > array[j + 1]; j--){
			swap(array[j], array[j + 1]);
		}
	}
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值