初阶数据结构——排序

news2024/11/24 10:48:06

目录

    • 排序的概念
    • 常见排序算法
      • 插入排序
      • 希尔排序
      • 选择排序
      • 堆排序
      • 冒泡排序
      • 快速排序
        • hoare
        • 挖坑法
        • 前后指针法
        • 快排的时间复杂度
        • 三路划分
        • 三数取中和随机数选中
        • 快排的非递归版本
        • 快速排序的总结
      • 归并排序
        • 归并的递归版本
        • 归并的非递归版本
      • 内排序和外排序
      • 非比较排序
      • 稳定性
      • 排序算法复杂度和稳定性

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

常见排序算法

在这里插入图片描述

插入排序

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
在这里插入图片描述

在这里插入图片描述

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n-1; ++i)
	{
		// [0, end] 有序,插入tmp依旧有序
		int end = i;
		int tmp = a[i+1];

		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}

		a[end + 1] = tmp;
	}
}

插入排序是O(N^2)中适应性较好的排序,在局部有序的情况下排序效率高

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

希尔排序

希尔排序法又称缩小增量法。

在这里插入图片描述
一组中的两个数交换:
在这里插入图片描述
排好一组:
在这里插入图片描述
排好gap组:
第一种方法:一组一组排
在这里插入图片描述
第二种方法:多组并排
在这里插入图片描述

一组一组排和多组并排两种效率都一样,写哪种都可以,写多组并排的多一些,因为可以写两层就写两层了。

gap大小意味着:
在这里插入图片描述

gap从大到小,从预排序到直接插入排序:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

gap = gap / 3 + 1保证了gap最后一次一定是1,比gap = gap / 3 然后再进行一次直接插入排序好
写gap = gap / 4 + 1,gap = gap / 2 什么的也可以,gap的取值有很多

测试希尔排序和插入排序谁更快

void TestOP()
{
	srand(time(0));
	int n = 500000;
	int* a1 = (int*)malloc(sizeof(int)*n);
	int* a2 = (int*)malloc(sizeof(int)*n);
	for (int i = 0; i < n; i++)
	{
		a1[i] = rand();
		a2[i] = a1[i];
	}

	int begin1 = clock();
	InsertSort(a1, n);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a1, n);
	int end2 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
}

int main()
{
	//TestInsert();
	//TestShell();
	TestOP();
	return 0;
}

clock获取的是程序开始执行到当前位置的时间点,两个clock之间的差距就是这个函数消耗的时间,单位是毫秒ms
在这里插入图片描述

学习各种排序是因为不同排序适应的场景不同

时间复杂度分析:

先单看红框
在这里插入图片描述
在这里插入图片描述
gap很大时:

gap=n/3,每组有3个数要比较,每组最坏挪动6次,有n/3组,合计6*n/3=2n,也就是n

gap=n/2,每组有2个数要比较,每组最坏挪动3次,有n/2组,合计3*n/2,也就是n

所以可以说gap很大时–>O(N)

把gap=1代入会发现是N^2,但因为经过了预排序,不可能是 N ^ 2

这个公式算是以最坏情况来算,可是越往后走不是最坏,因为前面的预排序会对后面的产生增益

所以难以计算gap不大不小时候的复杂度。

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算
  4. 稳定性:不稳定

《数据结构(C语言版)》— 严蔚敏
在这里插入图片描述

《数据结构-用面相对象方法与C++描述》— 殷人昆
在这里插入图片描述
我们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照:O(n^1.25) 到 O(1.6 * n^1.25) 来算。

我们可以认为整体时间复杂度取 O(N^1.3)较合适

选择排序

基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

优化:
选最值的时候可以同时选最大值,最小值

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin; i < end; i++)
		{
			if (a[i] < a[mini])
				mini = i;
			if (a[i] > a[maxi])
				maxi = i;
		}
		Swap(&a[begin], &a[mini]);
		//如果begin和maxi重叠
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);

		begin++;
		end--;
	}
}

注意begin可能会和maxi重叠的问题
在这里插入图片描述

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法

详细可看我的另一篇文章 二叉树

数组可能不是堆,但它是一个完全二叉树。
那么要排序的第一步是建堆

可以向上调整建堆,也可以向下调整建堆
向上调整的前提是:前面是堆
向下调整的前提是:左子树和右子树是大堆/小堆

在这里插入图片描述

注意排升序建大堆,排降序建小堆。

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		// 找出小的那个孩子
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 排升序
void HeapSort(int* a, int n)
{
	// 建大堆
	for (int i = (n-1-1)/2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

冒泡排序

冒泡排序就是把小的元素往前调或者把大的元素往后调。 比较是相邻的两个元素比较,交换也发生在这两个元素之间。

void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; ++j)
	{
		bool exchange = false;
		for (int i = 1; i < n-j; i++)
		{
			if (a[i - 1] > a[i])
			{
				int tmp = a[i];
				a[i] = a[i - 1];
				a[i - 1] = tmp;

				exchange = true;
			}
		}

		if (exchange == false)
		{
			break;
		}
	}
}

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

在这里插入图片描述

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort(a, begin, end);
	// [begin, keyi-1] keyi [keyi+1, end]

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

在这里插入图片描述

将区间按照基准值划分为左右两半部分的常见方式有:

hoare

left作为keyi,先右边找小,然后左边找大,左右找到的位置交换,最后让相遇位置和keyi位置交换,让keyi到正确的位置。
注意先后顺序,左边作keyi就要右边先找

int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}

		// 左边找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}

		Swap(&a[left], &a[right]);
	}

	Swap(&a[keyi], &a[left]);

	return left;
}

易错点:
在这里插入图片描述
左边作key,右边先走的原因:
在这里插入图片描述

挖坑法

选值作key(一般选left位置值),把该位置作为坑,先右边找小,找到了就和坑位置交换,然后变成新的坑,然后左边找小,也是交换然后变成新坑。最后坑位置就是key值应该在的位置。
在这里插入图片描述

// 挖坑法
// [left, right]
int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= key)
		{
			--right;
		}

		a[hole] = a[right];
		hole = right;

		// 左边找大
		while (left < right && a[left] <= key)
		{
			++left;
		}

		a[hole] = a[left];
		hole = left;
	}

	a[hole] = key;

	return hole;
}

前后指针法

在这里插入图片描述

// 前后指针法
// [left, right]
int PartSort3(int* a, int left, int right)
{
	int prev = left;
	int cur = left+1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}

		++cur;
	}

	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}

快排的时间复杂度

在这里插入图片描述
当每次选key都是中位数,效率就很好,但是数组有序的时候,就是快排最坏的情况,时间复杂度是O(N^2)

三路划分

测试排序的OJ

当我们用快排跑OJ的时候会发现超时

在这里插入图片描述
针对这种测试用例,我们可以采用三路划分的方法
在这里插入图片描述
三路划分的方法有点像hoare和前后指针的结合,前后指针是把大的值往后推,三路划分是把和key相等的值往中间推。

在这里插入图片描述

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

    int key = a[left];
    int l=left,r=right,c=left+1;
    //三路划分
    //a[c]<key,交换c和l的位置,++l,++c
    //a[c]>key,交换c和r的位置,--r
    //a[c]==key,++c
	while (c<=r)
	{
        if(a[c]<key)
        {
            Swap(&a[l], &a[c]);
            ++l;
            ++c;
        }
        else if(a[c]>key)
        {
            Swap(&a[r], &a[c]);
            --r;
        }
        else
        {
            ++c;
        }
	}
    //[begin,l-1] [l,r] [r+1,end]

	QuickSort(a, begin, l - 1);
	QuickSort(a, r+1, end);
}

三路划分和普通划分相比效率会略低一些,c和r的交换会费事,但是三里路划分全面,应对的场景更多

三数取中和随机数选中

传统的三数取中也会被测试用例卡住,比如left,right,和中间位置都是接近最小,最大的值,那么我们可以不用固定的三数取中,而用随机数选中

int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

改变后:

int GetMidIndex(int* a, int left, int right)
{
	int mid = left+(rand()%(right-left));
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

int* sortArray(int* nums, int numsSize, int* returnSize){
    srand(time(0));
    QuickSort(nums,0,numsSize-1);
    *returnSize=numsSize;
    return nums;
}

快排的非递归版本

在这里插入图片描述

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	STPush(&st, end);
	STPush(&st, begin);

	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);

		int right = STTop(&st);
		STPop(&st);

		int keyi = PartSort1(a, left, right);

		// [left, keyi-1] keyi [keyi+1, right]

		if (keyi + 1 < right)
		{
			STPush(&st, right);
			STPush(&st, keyi + 1);
		}

		if (left < keyi-1)
		{
			STPush(&st, keyi-1);
			STPush(&st, left);
		}
	}

	STDestroy(&st);
}

快速排序的总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定

归并排序

分治的思想,将已有序的子序列合并,得到完全有序的序列
在这里插入图片描述

归并的递归版本

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin == end)
		return;

	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end]
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid+1, end, tmp);

	// 归并两个区间
	// ...

	int begin1 = begin, end1 = mid;
	int begin2 = mid+1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a+begin, tmp+begin, sizeof(int) * (end - begin + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);

	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
}

改进:小区间优化

只针对递归版本
在这里插入图片描述
只是为了让8个数有序,付出的代价有点高,递归次数很多
大区间不断切割,当切到小区间的时候,为了让小区间有序就没必要再用递归这种方式,可以考虑用其他排序。用简单的排序就可以,比如直接插入排序。

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin == end)
		return;

	// 小区间优化
	if (end - begin + 1 < 10)
	{
		InsertSort(a+begin, end - begin + 1);

		return;
	}

	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end]
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid+1, end, tmp);

	// 归并两个区间
	// ...

	int begin1 = begin, end1 = mid;
	int begin2 = mid+1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a+begin, tmp+begin, sizeof(int) * (end - begin + 1));
}

归并的非递归版本

在这里插入图片描述
坑点:

数据是2的次方的时候理想情况,但是不理想情况时会存在很多问题
在这里插入图片描述
针对越界问题有两种方法,第一种方法好理解

1、遇到begin2或者end1越界就break,遇到end2越界就修正end2=n-1。但是要注意拷贝的时候不能一次性全部拷贝,不然就会从tmp中拷贝随机值覆盖掉a数组中的数据。得归并一组就拷贝一组。

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);

	// 1  2  4 ....
	int gap = 1;
	while (gap < n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2 * gap)
		{
			// 每组的合并数据
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			printf("[%d,%d][%d,%d]\n", begin1, end1, begin2, end2);

			if (end1 >= n || begin2 >= n)
			{
				break;
			}

			// 修正
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			// 归并一组,拷贝一组
			memcpy(a+i, tmp+i, sizeof(int)*(end2-i+1));
		}
		printf("\n");
		
		//memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}

	free(tmp);
}

2、不管是begin2,end1,还是end2越界我们都进行修正,修正成不存在的区间 while (begin1 <= end1 && begin2 <= end2)就不会出现越界,然后就可以采用全部拷贝。

注意遇到end1,begin2越界时不能把begin2和end2修成n-1,这样会导致n-1位置的值会多次合并

错误示范:
在这里插入图片描述

正确示范:
在这里插入图片描述

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);

	// 1  2  4 ....
	int gap = 1;
	while (gap < n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2 * gap)
		{
			// 每组的合并数据
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			printf("修正前:[%d,%d][%d,%d]\n", begin1, end1, begin2, end2);

			if (end1 >= n)
			{
				end1 = n - 1;

				// 不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				// 不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if(end2 >= n)
			{
				end2 = n - 1;
			}

			printf("修正后:[%d,%d][%d,%d]\n", begin1, end1, begin2, end2);

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}
		}
		printf("\n");
		
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}

	free(tmp);
}

内排序和外排序

在这里插入图片描述
对40G的文件如何排序,可以考虑递归分割的方式去排,其他排序一般只适用于内排序,而归并排序既适用于内排序也适用于外排序。
文件不能用其他排序的思想,因为文件是不能用下标随机访问的。

在这里插入图片描述

把100亿整数的文件切成40个小文件,
先把1G读到内存中,然后用快排等排序好,(归并有空间复杂度的消耗,所以不用)。然后两两归并。

让小文件有序就在内存中排序,再写回小文件。
大的文件归并的话,是在磁盘上归并,比如用文件指针打开不同的文件,然后从文件中读取整数,小的尾插(写到新文件),用文件接口的方式将两个小文件归并出一个大文件。

非比较排序

基数排序
按个位十位百位去排序,在实际中意义不大

在这里插入图片描述

void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] < min)
		{
			min = a[i];
		}

		if (a[i] > max)
		{
			max = a[i];
		}
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int) * range);
	memset(countA, 0, sizeof(int) * range);

	// 统计次数
	for (int i = 0; i < n; i++)
	{
		countA[a[i] - min]++;
	}

	// 排序
	int k = 0;
	for (int j = 0; j < range; j++)
	{
		while (countA[j]--)
		{
			a[k++] = j + min;
		}
	}
}

缺陷1:依赖数据范围,适用于范围集中的数组
缺陷2:只能用于整形

时间复杂度:O(N+Range)
空间复杂度:O(Range)
稳定性:稳定

稳定性

在这里插入图片描述
在这里插入图片描述

排序算法复杂度和稳定性

排序方法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(N^2)O(N)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
直接插入排序O(N^2)O(N)O(N^2)O(1)稳定
希尔排序O(n logn) ~ O(N^2)O(N^1.3)O(N^2)O(1)不稳定
堆排序O(n logn)O(n logn)O(n logn)O(1)不稳定
归并排序O(n logn)O(n logn)O(n logn)O(n)稳定
快速排序O(n logn)O(n logn)O(N^2)O(logn) ~ O(n)不稳定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/766320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试基础 Android 应用测试总结

目录 启动&#xff1a; 功能介绍&#xff0c;引导图&#xff0c;流量提示等&#xff1a; 权限&#xff1a; 文件错误 屏幕旋转&#xff1a; 流量&#xff1a; 缓存&#xff08;/sdcard/data/com.your.package/cache/&#xff09;&#xff1a; 正常中断&#xff1a; 异…

Android 自定义CheckBox样式,设置切换背景图,类似于RadioButton

文章目录 概要自定义CheckBok资源文件如下使用方法实现效果 概要 目前要实现类似于Radiobutton选择按钮&#xff0c;如果只有一个RadioButton&#xff0c;就不能和radio Group连用&#xff0c;导致选择没办法取消&#xff0c;如果要实现只能代码中进行操作&#xff0c;过于繁琐…

项目管理软件选择指南:最佳实践与避坑指南

当今企业中&#xff0c;协作工具是必不可少的&#xff0c;每个企业都会寻找最适合自己的协作工具来提高工作效率。在这些协作工具中&#xff0c;Zoho Projects项目协作工具是最常用的一种&#xff0c;因为它能够为团队提供一个集任务、项目、文档、IM、目标、日历、甘特图、工时…

MIT 6.829 -- L2 The Internetworking Problem

MIT 6.829 -- L2 The Internetworking Problem 前言The Internetworking Problem: Many Different NetworksGateWays互联网设计原则通用性原则健壮性原则互联网缺点互联网协议标准流程 最早的TCP/IP今天的TCP/IP: IPv4地址分片和重组Time-to-live&#xff08;TTL&#xff09;Ty…

2023 7.17~7.23 周报 (最近读的论文方法论分析)

0 上周回顾 上周完成了RTM的研究学习, 完成了进一步阅读论文所需的知识储备. 同时从代码层面深度解析了正演和RTM存在的关系, 发掘了很多富有参考意义的信息. 1 本周计划 深度剖析论文《Deep-Learning Full-Waveform Inversion Using Seismic Migration Images》的方法体系,…

计算机网络——VLan介绍

学习视频&#xff1a; 网工必会&#xff0c;十分钟搞明白&#xff0c;最常用的VLAN技术_哔哩哔哩_bilibili 技术总结&#xff1a;VLAN&#xff0c;网络中最常用的技术&#xff0c;没有之一_哔哩哔哩_bilibili 全国也没几个比我讲得好的&#xff1a;VLAN虚拟局域网 本来补充了…

微服务day1——微服务入门

一、认识微服务 1、单体架构 将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署。 优点 架构简单部署成本低 缺点 耦合度高 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fPfsQXAn-1689593800699)(https://picture.wangkay.tec…

探索OLED拼接屏的特点及在莱山的场景化应用

涞山oled拼接屏是一种高清晰度的显示屏&#xff0c;由多个oled屏幕拼接而成。它可以用于各种场合&#xff0c;如商业展示、广告宣传、会议演示等。涞山oled拼接屏具有以下特点&#xff1a; 1. 高清晰度&#xff1a;oled屏幕具有高对比度、高亮度、高色彩饱和度等特点&#xff0…

jeecg-boot sql注入漏洞解决

输入下面的链接地址&#xff0c;就会出现下面的获取数据&#xff0c;这个漏洞还是比较严重的啊 http://localhost:8080/nbcio-boot/sys/ng-alain/getDictItemsByTable/%20from%20sys_user/*,%20/x.js 通过上面方式可以获取用户信息了 如下&#xff1a; RequestMapping(valu…

层次分析模型

层次分析法是对一些较为复杂、模糊的问题做出决策的简易方法 这里涉及一个决策概念的理解 初步理解应该是一种评价类的模型 层次分析法的典型应用&#xff1a; 1、用于最佳方案的选取 2、用于评价类分析 3、用于指标体系的优选 层次分析法的名字中层次的原因 层次分析法的步骤…

Skywalking使用说明

需求背景 随着分布式的盛行&#xff0c;系统的复杂度也逐步增加&#xff0c;不同服务间的交互对性能的定位提出了更高的要求。任意一个节点的异常&#xff0c;都可能对业务系统造成损失。对于链路追踪&#xff0c;迫切需要一个优秀的监测工具。 需求如下 功能性需求 请求链…

怎么把word文档转换成pdf文件?这三个方法超级实用!

word文档编辑完成后&#xff0c;通常会将其转换为PDF格式&#xff0c;以使文档内容更加简洁。那么&#xff0c;如何将Word文档转换为PDF呢&#xff1f;下面将介绍三种方法&#xff0c;相信对你会有所帮助。 一、记灵在线工具 首先&#xff0c;在浏览器中打开记灵在线工具的网…

Spring+SpringMVC+JdbcTemplate小Demo

项目目录结构 创建mavenWeb项目 pom文件 spring依赖、spring-web依赖、spring-webmvc依赖、spring-test、junit依赖 servlet依赖、jsp依赖、jstl、standard依赖 jackson的core和databind和annotations依赖、fastjson依赖、 文件上传的commons-fileupload和commons-io依赖 日志c…

图片修补 EdgeConnect 论文的阅读与翻译:生成边缘轮廓先验,再填补缺失内容

本文将要介绍的论文就是&#xff1a;EdgeConnect: Generative Image Inpainting with Adversarial Edge Learning&#xff0c;因为知乎在&#xff08;2019-02-02&#xff09;前&#xff0c;缺少详细介绍这篇论文的文章&#xff0c;而我最近需要复现它&#xff0c;所以顺便在这里…

消息重试框架 Spring-Retry 和 Guava-Retry

一 重试框架之Spring-Retry 1.Spring-Retry的普通使用方式 2.Spring-Retry的注解使用方式 二 重试框架之Guava-Retry 总结 图片 一 重试框架之Spring-Retry Spring Retry 为 Spring 应用程序提供了声明性重试支持。它用于Spring批处理、Spring集成、Apache Hadoop(等等)。…

智能应急疏散系统在公共建筑中的的应用

安科瑞 华楠 摘 要&#xff1a;随着大型公共建筑物的不断增多&#xff0c;其所产生的各种建筑安全隐患问题也在逐渐加剧&#xff0c;一旦出现火灾险情&#xff0c;要想从公共建筑中安全的脱离出来&#xff0c;其难度也是可想而知。因此&#xff0c;这就需要在进行公共建筑设计时…

Java打怪升级路线的相关知识

第一关:JavaSE阶段 1、计算机基础 2、java入门学习 3、java基础语法 4、流程控制和方法 5、数组 6、面向对象编程 7、异常 8、常用类 9、集合框架 10、IO 11、多线程 12、GUI编程 13、网络编程 14、注解与反射 15、JUC编程 16、JVM探究 17、23种设计模式 18、数据结构与算法 1…

mysql数字开头字符串排序

表结构 CREATE TABLE building (id bigint NOT NULL,name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT 名称,full_name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT 全称,PRIMARY KEY (id) USIN…

Qt|读写ini文件使用QSettings 节键值 设置相对路径

#include <QtWidgets/QApplication> #include <QWidget> #include <QSettings> #include <QString>int main(int argc, char *argv[]) {QApplication a(argc, argv);// Qt中使用QSettings类读写ini文件// QSettings构造函数的第一个参数是ini文件的路径…

linux之Ubuntu系列(三)远程管理指令☞Scp

cp scp cp 复制文件 是限制在本地操作 scp&#xff1a; 远程拷贝文件 cp [options] 源文件or 目录 目标文件or 目录 如果复制目录&#xff0c;要加 -r 选项 &#xff0c;同时如果目标目录不存在&#xff0c;会会创建 scp scp就是 secure copy&#xff0c;是一个在linux下用来…