排序算法知识点和常见面试题

查找和排序算法知识点和常见面试题

查找

顺序查找

时间复杂度:O(n)

二分查找

时间复杂度:O(logn)
1.low high两个指针分别指向数组头和尾
2.mid = (low + high) / 2,并将nums[mid]与target比较
3.如果相等,返回;如果小于,则low移动;如果大于,则high移动
4.否则返回-1

int binary_search(int array[], int value, int size)
{
    int low = 0;
    int high = size -1;
    int mid;
    while(low <= high)
    {
        mid = (low + high) / 2;     // 二分
        if(array[mid] == value)     // 中间数据是目标数据
            return mid;
        else if(array[mid] < value) // 中间数据比目标数据小
            low = mid + 1;
        else   // 中间数据比目标数据大
            high = mid - 1;
    }
    return -1;
}
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int low = 0;
        int high = nums.size()-1;
        while(low <= high){
            int middle = (low + high) / 2; //int middle = left + ((right - left) / 2);//防止溢出

            if(nums[middle] < target){
                low =  middle + 1;
            }
            else if(nums[middle] > target){
                high = middle - 1;
            }
            else
                return middle;
        }
        return -1;
    }
};

哈希表查找

需要额外空间O(n)实现哈希表,查找时时间复杂度为O(1),用空间换时间。

二叉树查找

二叉树查找:在二叉树查找值为k的过程中,需要比较的次数等于值为k所代表的结点在二叉树中的层数。因此比较次数最少为1次(k值恰好在根结点),最多不超过树的深度。具有n个结点的二叉树的深度至少是[]+1,至多为n。故:
①如果二叉树是平衡的,那么时间复杂度为O(logn);
②如果二叉排序树完全不平衡,那么时间复杂度为O(n)。


bool search(Node* root, int key)
{
	while (root != NULL)
	{
		if (key == root->data)
			return true;
		else if (key < root->data)
			root = root->left;
		else
			root = root->right;
	}
	return false;

排序算法知识点

(冒泡 插入 选择) (快速 希尔) (堆 归并)
n2(n2) n2(nlogn) nlogn(nlogn) 最坏(平均)
稳 不稳 稳

冒泡排序

O(n2)


vector<int> bobbleSort(vector<int>& nums) {
	for (int i = 0; i < nums.size() - 1; i++) {    // n-1趟
		for (int j = 0; j < nums.size() - 1 - i; j++) {  // 未排好序区间
			if (nums[j] > nums[j + 1]) {
				int tmp = nums[j];      // 交换
				nums[j] = nums[j + 1];
				nums[j + 1] = tmp;
			}
		}
	}
	return nums;
}


//改进版,一趟未交换说明已经排好序了
vector<int> bobbleSort1(vector<int>& nums) {
	for (int i = 0; i < nums.size() - 1; i++) {
		bool exchange = false;   // 标志位
		for (int j = 0; j < nums.size() - 1 - i; j++) {
			if (nums[j] > nums[j + 1]) {
				int tmp = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = tmp;
				exchange = true;  // 交换了标志位改变
			}
		}
		if (!exchange) return nums;
	}
	return nums;
}

插入排序

每次选数插入到适合位置,即已经选好的数排好序

vector<int> insertSort(vector<int>& nums) {
	for (int i = 1; i < nums.size(); i++) {
		int tmp = nums[i];  // 摸到的数
		int index = i - 1;  // 手里数下标
		while (index >= 0 && nums[index] > tmp) {
			nums[index + 1] = nums[index]; // 右移
			index--;
		}
		nums[index + 1] = tmp;  // 找到适合位置放入
	}
	return nums;
}

选择排序

每次选择最小值排序


vector<int> selectSort(vector<int>& nums) {
	vector<int> res;
	for (int i = 0; i < nums.size(); i++) {
		int index = 0, minVal = nums[0];
		for (int j = 1; j < nums.size(); j++) {  // 获得最小值
			if (nums[j] < minVal) {
				index = j;
				minVal = nums[j];
			}
		}
		res.push_back(minVal);
		nums[index] = 1000;   // 最小值位置用一个较大数覆盖
	}
	return res;

//改进版(原地排序)
vector<int> selectSort1(vector<int>& nums) {
	for (int i = 0; i < nums.size() - 1; i++) {
		int index = i;
		for (int j = i + 1; j < nums.size(); j++) {
			if (nums[index] > nums[j]) index = j;  // 确定最小值索引
		}
		if (index != i) {  // 不是原位置才需交换
			int tmp = nums[i];
			nums[i] = nums[index];
			nums[index] = tmp;
		}
	}
	return nums;
}

快速排序

二分思维+递归思维
1.选取一个数,使这个数归位
2.即数组被分为两部分,左边都比这个数小,右边都比这个数大
3.递归完成排序

int partition(vector<int>& nums, int left, int right) {
	int tmp = nums[left];  // 选取的数
	while (left < right) {
		while (left < right && nums[right] >= tmp) right--;
		nums[left] = nums[right];  // 小数放左边
		while (left < right && nums[left] <= tmp) left++;
		nums[right] = nums[left];  // 大数放右边
	}
	nums[left] = tmp;  // 选取的数放回数组
	return left;
}
 
vector<int> quickSort(vector<int>& nums, int left, int right) {
	if (left < right) {
		int mid = partition(nums, left, right);  // 归位函数
		quickSort(nums, left, mid - 1);
		quickSort(nums, mid + 1, right);
	}
	return nums;

希尔排序

又称缩小增量排序,希尔排序的基本思想是先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插人排序。


#include <iostream> 
using namespace std;
void Print(int array[],int length){	//每执行一次打印一次序列 
	for(int i=0;i<length;i++){
		cout<<array[i]<<" ";
	}
	cout<<endl;
}
void ShellSort(int array[],int length){
	for(int step=length/2;step>0;step=step/2){//初始步长step为length/2 
		for(int i=0;i<step;i++){	//遍历每一次分组后的第一个元素 
			for(int j=i+step;j<length;j+=step){	//遍历步长为step的所有元素 ,进行直接插入排序 
				int temp=array[j];
				int m=j-step;
				for(m=j-step;m>=0&&array[m]>temp;m-=step){//array[m]小于 temp 时循环结束,结束后应将 temp 赋值给a[m+step]
					array[m+step]=array[m];
				}
				array[m+step]=temp;		//将 temp 赋值给a[m+step]
			}
		}
	Print(array,length);	//每排序一趟就打印一次序列 
	}
}
int main(){
	int array[]={49,38,65,97,76,13,27,49};
	int length=sizeof(array)/sizeof(*array);
	Print(array,length);			//先打印原始序列 
	ShellSort(array,length);
	return 0;
}

堆排序

1.建堆---农村包围城市(从最后一个孩子节点的父节点开始一步步使得子树有序)
2.得到堆顶元素为最大值
3.去掉堆顶,将最后一个元素置于堆顶,通过一次向下调整使堆有序
4.堆顶为第二大元素
5.重复步骤3

常用于topk问题


// 大根堆------父节点的值大于左右孩子节点的值
void siftLarge(vector<int>& nums, int low, int high) {
	int i = low;  // 根节点
	int j = 2 * i + 1;  // 左孩子节点
	int tmp = nums[low]; // 堆顶元素
	while (j <= high) {
		if (j + 1 <= high && nums[j] < nums[j + 1]) j++;  // 右孩子大
		if (nums[j] > tmp) {
			nums[i] = nums[j];
			i = j;
			j = 2 * i + 1;
		}
		else {
			nums[i] = tmp;  // 找到适合位置放入
			break;
		}
	}
	nums[i] = tmp;  // 放在叶子节点
}
 
vector<int> heapSort(vector<int>& nums) {
	int n = nums.size() - 1;
	for (int i = (n - 1) / 2; i >= 0; i--) {  // 建堆
		siftLarge(nums, i, n);
	}
	for (int i = n; i >= 0; i--) {
		int tmp = nums[0];   // 交换堆顶和最后一个元素
		nums[0] = nums[i];
		nums[i] = tmp;
		siftLarge(nums, 0, i-1);
	}
	return nums;
}

归并排序

1.分解------将数组越分越小,直到分成一个元素
2.终止条件------一个元素是有序的
3.合并------将元素数组合并,越来越大

void merge(vector<int>& nums, int left, int mid, int right) {
	vector<int> ltmp;
	int i = left;
	int j = mid + 1;
	while (i <= mid && j <= right) {  // 两边都有数
		if (nums[i] < nums[j]) {
			ltmp.push_back(nums[i]);
			i++;
		}
		else {
			ltmp.push_back(nums[j]);
			j++;
		}
	}
	while (i <= mid) {     // 一边没数
		ltmp.push_back(nums[i]);
		i++;
	}
	while (j <= right) {
		ltmp.push_back(nums[j]);
		j++;
	}
	int index = 0;   // 更新nusm
	for (int k = left; k <= right; k++) {
		nums[k] = ltmp[index];
		index++;
	}
}
 
vector<int> mergeSort(vector<int>& nums, int left, int right) {
	if (left < right) {
		int mid = left + (right - left) / 2;
		mergeSort(nums, left, mid);
		mergeSort(nums, mid + 1, right);
		merge(nums, left, mid, right);
	}
	return nums;
}

常见面试题

posted @ 2023-09-04 16:39  我好想睡觉啊  阅读(8)  评论(0编辑  收藏  举报