排序算法(2)之选择排序----直接选择排序和堆排序

news2024/11/18 11:38:46

 个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

排序算法(2)之交换排序----冒泡排序和堆排序

收录于专栏【数据结构初阶
本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1.前置说明 

2.选择排序

2.1选择排序的基本思想

2.2直接选择排序

2.2.1直接选择排序的概念

2.2.2直接选择排序的示例

2.2.3动图演示

2.2.4代码展示 

2.2.4.1未经优化的选择排序

2.2.4.2优化后的选择排序

2.2.5测试代码 

2.2.5.1未经优化

2.2.5.2经过优化

2.2.6时间复杂度分析

2.3堆排序

2.3.1堆排序的概念

2.3.2 堆排序示例

2.3.3堆排序图形演示

2.3.4代码展示

2.3.4.1向下调整建堆

2.3.4.2向上调整建堆

2.3.5测试代码

2.3.6时间复杂度分析

2. 排序过程的时间复杂度

3.直接选择排序与堆排序的比较

总结比较

4.总结


1.前置说明 

关于排序的概念及其运用大家可以参考下面的文章,这里就不重复讲述:

今天讲的是选择排序中的直接选择排序和堆排序

排序算法(1)之插入排序----直接插入排序和希尔排序-CSDN博客

 

2.选择排序

2.1选择排序的基本思想

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

2.2直接选择排序

2.2.1直接选择排序的概念

直接选择排序(Selection Sort)是一种简单直观的排序算法,其基本思想是每次从待排序的数据元素中选择最小(或最大)的一个元素,依次放到已排序序列的末尾,直到所有元素均排序完成为止。

具体步骤如下:

  • 首先,在未排序序列中找到最小(或最大)的元素,存放到排序序列的起始位置。
  • 然后,从剩余未排序元素中继续寻找最小(或最大)的元素,放到已排序序列的末尾。
  • 依次类推,直到所有元素均排序完毕。 

2.2.2直接选择排序的示例

假设我们有一个数组 [64, 25, 12, 22, 11] 需要按照从小到大的顺序进行排序。

第一步: 初始状态,整个数组为 [64, 25, 12, 22, 11]

  1. 第一次迭代

    • 找到当前未排序部分的最小值,即数组中的 11
    • 将 11 与第一个元素 64 交换位置,得到数组 [11, 25, 12, 22, 64]
    • 此时,第一个位置 [11] 可以看作已排序部分,而剩余的未排序部分为 [25, 12, 22, 64]
  2. 第二次迭代

    • 在剩余的数组 [25, 12, 22, 64] 中找到最小值 12
    • 将 12 与第二个位置的 25 交换位置,得到数组 [11, 12, 25, 22, 64]
    • 现在,前两个位置 [11, 12] 已排序,剩余的未排序部分为 [25, 22, 64]
  3. 第三次迭代

    • 在剩余的数组 [25, 22, 64] 中找到最小值 22
    • 将 22 与第三个位置的 25 交换位置,得到数组 [11, 12, 22, 25, 64]
    • 现在,前三个位置 [11, 12, 22] 已排序,剩余的未排序部分为 [25, 64]
  4. 第四次迭代

    • 在剩余的数组 [25, 64] 中找到最小值 25
    • 将 25 与第四个位置的 25(其实是自身,无需交换)位置,得到数组 [11, 12, 22, 25, 64]
    • 现在,整个数组都已排序完成。

排序过程总结为:

  • 第一次迭代找到 11,交换得到 [11, 25, 12, 22, 64]
  • 第二次迭代找到 12,交换得到 [11, 12, 25, 22, 64]
  • 第三次迭代找到 22,交换得到 [11, 12, 22, 25, 64]
  • 第四次迭代找到 25,无需交换,数组最终有序。

这样,通过反复选择未排序部分的最小元素,并与未排序部分的第一个元素交换位置,直到整个数组排序完成,即完成了直接选择排序的过程。

2.2.3动图演示

2.2.4代码展示 

2.2.4.1未经优化的选择排序
void Swap(int* p1, int* p2)
{
	int a = *p1;
	*p1 = *p2;
	*p2 = a;
}
void SelectSort1(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int mini = i;
		for (int j = i; j < n; j++)
		{
			if (a[mini] > a[j])
				mini = j;
		}
		Swap(&a[i], &a[mini]);
	}
}
2.2.4.2优化后的选择排序
void Swap(int* p1, int* p2)
{
	int a = *p1;
	*p1 = *p2;
	*p2 = a;
}
void SelectSort2(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		if (begin == maxi)
			maxi = mini;

		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

分析: 

  1. 函数定义

    void SelectSort2(int* a, int n)
    • 函数名为 SelectSort2,返回类型为 void,表示不返回任何值。
    • 函数接受两个参数:a 是指向整型数组的指针,n 是数组中元素的个数。
  2. 选择排序的变种实现

    • 首先定义了两个变量 begin 和 end,分别初始化为数组的起始索引 0 和末尾索引 n-1
    • 进入 while 循环,条件是 begin < end,即未排序部分至少有两个元素。
  3. 内部循环

    • 在每次循环开始时,初始化 mini 和 maxi 分别为 begin,意味着假定当前未排序部分的第一个元素为最小和最大。
    • 通过 for 循环遍历 begin+1 到 end 之间的元素:
      • 如果发现比当前 maxi 索引指向的元素大的元素,则更新 maxi
      • 如果发现比当前 mini 索引指向的元素小的元素,则更新 mini
  4. 交换操作

    • 在每一轮循环结束后,通过 Swap 函数交换 begin 索引和 mini 索引所指向的元素,确保当前未排序部分的最小元素被放置在已排序部分的末尾。
    • 如果 begin 等于 maxi,则在交换后需要更新 maxi 为 mini 的值。
    • 接着,再次通过 Swap 函数交换 end 索引和 maxi 索引所指向的元素,确保当前未排序部分的最大元素被放置在已排序部分的开头。
  5. 循环控制

    • 每完成一轮循环,即一个元素被正确地放置在已排序的位置上,递增 begin 并递减 end,缩小未排序部分的范围,直至 begin 不再小于 end,排序完成。
  6. 功能分析

    • 这种选择排序的变种在每一轮循环中同时找到未排序部分的最小值和最大值,并将它们分别放置在已排序部分的开头和末尾。
    • 效果上比传统的选择排序稍微快一些,因为减少了每轮循环中的交换次数,但其时间复杂度仍为 O(n^2),并未改变选择排序的基本特性。

注意: 

        Swap(&a[begin], &a[mini]);
        if (begin == maxi)
            maxi = mini;

这段代码是为了防止在第一次交换后,maxi和mini已经进行过交换,将它们回复原位

2.2.5测试代码 

--测试oj链接--912. 排序数组 - 力扣(LeetCode)

2.2.5.1未经优化

代码展示:

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

void SelectSort1(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int mini = i;
		for (int j = i; j < n; j++)
		{
			if (a[mini] > a[j])
				mini = j;
		}
		Swap(&a[i], &a[mini]);
	}
}
int* sortArray(int* nums, int numsSize, int* returnSize) {
    (*returnSize) = numsSize;
    int* array = (int*)malloc(sizeof(int)*(*returnSize));
    for(int i = 0; i < numsSize; i++)
    {
        array[i] = nums[i];
    }
    SelectSort1(array, numsSize);
    return array;
}

结果分析: 

很遗憾,只过了10个例子就超时,比插入排序还要烂......

2.2.5.2经过优化

代码展示:

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

void SelectSort2(int* a, int n)
{
	int begin = 0, end = n-1;
    while(begin < end)
    {
        int mini = begin, maxi = begin;
        for(int i = begin + 1; i <= end; i++)
        {
            if(a[i] < a[mini])
                mini = i;
            if(a[i] > a[maxi])
                maxi = i; 
        }
        Swap(&a[begin], &a[mini]);
        if(begin == maxi)
            maxi = mini;
        Swap(&a[end], &a[maxi]);
        ++begin;
        --end;
    }
}
int* sortArray(int* nums, int numsSize, int* returnSize) {
    (*returnSize) = numsSize;
    int* array = (int*)malloc(sizeof(int)*(*returnSize));
    for(int i = 0; i < numsSize; i++)
    {
        array[i] = nums[i];
    }
    SelectSort2(array, numsSize);
    return array;
}

结果分析:

优化后还是只能通过10个例子,效率还是很低....

2.2.6时间复杂度分析

  • 时间复杂度为 (O(n^2)),其中 (n) 是待排序元素的数量。
  • 空间复杂度为 (O(1)),因为直接选择排序是原地排序算法,不需要额外的存储空间。
  • 稳定性:直接选择排序是不稳定的,因为交换操作可能改变相同元素的相对顺序。

2.3堆排序

因为这里是具体讲排序的章节,所以不会具体分析堆的实现,大家可以去下面的链接自行查看

--数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)-CSDN博客

2.3.1堆排序的概念

堆排序(Heap Sort)是一种基于堆数据结构的排序算法,其特点是在排序过程中使用了最大堆或最小堆这种特殊的完全二叉树结构。堆是一种满足堆属性的二叉树:对于最大堆,父节点的值始终大于或等于其子节点的值;对于最小堆,则父节点的值始终小于或等于其子节点的值。

堆排序的步骤:

  1. 构建堆

    • 将待排序的序列构建成一个堆。根据排序的要求,可以构建最大堆(升序排序使用)或最小堆(降序排序使用)。
    • 构建堆的过程可以通过自下而上的方式,从最后一个非叶子节点开始,依次向上调整每个子树,使其满足堆的性质。调整过程称为堆化(Heapify)。
  2. 堆排序

    • 利用堆的特性,每次从堆顶(根节点)取出最大或最小的元素,与堆的最后一个元素交换。
    • 交换后,堆的规模减小一,然后再通过堆调整(Heapify)重新将剩余的元素重新调整为堆。
    • 重复以上步骤,直到堆中只剩下一个元素,此时所有元素都已经有序排列。 

2.3.2 堆排序示例

当我们使用堆排序时,可以通过一个简单的例子来说明其工作原理。假设我们有一个待排序的数组 [4, 10, 3, 5, 1],我们希望按升序排序这个数组。

步骤一:构建最大堆

  1. 初始数组[4, 10, 3, 5, 1]

  2. 从最后一个非叶子节点开始进行堆化

    • 最后一个非叶子节点索引是 (n // 2) - 1 = (5 // 2) - 1 = 1,对应元素为 10
    • 开始堆化过程,使得整个数组满足最大堆的性质。

    堆化后的数组:[10, 5, 3, 4, 1]

  3. 继续堆化

    • 对于根节点 10,将其与子节点 3 和 4 进行比较和交换,确保最大堆性质。
    • 堆化后的数组:[10, 5, 3, 4, 1]

步骤二:堆排序

  1. 交换堆顶元素与末尾元素

    • 将堆顶元素 10 与末尾元素 1 交换,此时数组末尾是已排序部分。
    • 数组变为:[1, 5, 3, 4, 10]
  2. 重新堆化

    • 对剩余的元素 [1, 5, 3, 4] 进行堆化,重新构建最大堆。

    堆化后的数组:[5, 4, 3, 1]

  3. 继续排序

    • 将堆顶元素 5 与末尾元素 1 交换,数组末尾再添加一个已排序元素。
    • 数组变为:[1, 4, 3, 5]
  4. 最后一步

    • 将堆顶元素 4 与末尾元素 3 交换。
    • 数组变为:[1, 3, 4, 5]

完成排序

最终排序后的数组为 [1, 3, 4, 5, 10],这就是堆排序按升序排列给定的数组的过程。

2.3.3堆排序图形演示

向上调整建堆

向下调整建堆

2.3.4代码展示

2.3.4.1向下调整建堆
void Swap(int* p1, int* p2)
{
	int a = *p1;
	*p1 = *p2;
	*p2 = a;
}

void AdjustDown(int* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // 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)
{
	// 向下调整建堆 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

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

分析:

  1. Swap 函数:

    • 实现了两个整数指针指向的值的交换。
  2. AdjustDown 函数:

    • 用于向下调整堆结构,保持堆的性质(大顶堆),其中 a 是待调整的数组,n 是数组长度,parent 是需要调整的父节点索引。
    • 该函数首先假设左孩子较小,然后找出实际较大的孩子,将较大的孩子与父节点比较并交换,然后递归调整被交换的孩子节点。
  3. HeapSort 函数:

    • 先通过向下调整(AdjustDown 函数)建立初始的大顶堆。从最后一个非叶子节点开始,依次向上调整堆,确保每个节点都满足堆的性质。
    • 排序阶段,从堆顶(最大值)开始,将堆顶元素与当前未排序部分的末尾元素交换,然后重新调整堆顶,继续这个过程直到所有元素都被排序。

 

2.3.4.2向上调整建堆

代码展示:

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

void AdjustUp(int* a, int n, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void AdjustDown(int* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // 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 HeapSort2(int* a, int n)
{
	// 向下上调整建堆 O(N*logN)
	for (int i = 0; i < n; i++)
	{
		AdjustUp(a, n, i);
	}

	printf("\n");

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


}

分析:

  1. Swap 函数

    • 这个函数用于交换两个整数指针所指向的值,用于数组元素的位置交换。
  2. AdjustUp 函数

    • 这个函数用于向上调整堆结构,确保在数组的某个位置(child)插入新元素后,仍然保持堆的性质(大顶堆)。
    • 从给定的孩子节点开始,不断与其父节点比较并交换,直到满足堆的条件或者已经到达根节点。
  3. AdjustDown 函数

    • 这个函数用于向下调整堆结构,确保在删除堆顶元素后,剩余的数组依然是一个大顶堆。
    • 从指定的父节点开始,找出其两个孩子中较大的一个,然后与父节点比较并交换,重复这个过程直到满足堆的性质或者到达叶子节点。
  4. HeapSort2 函数

    • 这是堆排序的主函数。首先通过调用 AdjustUp 函数,将整个数组构建成一个大顶堆。
    • 然后,通过交换堆顶元素和当前堆的末尾元素,并调用 AdjustDown 函数来恢复堆的性质。重复这个过程直到整个数组排序完成。

2.3.5测试代码

oj链接:912. 排序数组 - 力扣(LeetCode)

代码展示:

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

void AdjustUp(int* a, int n, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//while (parent >= 0)
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void AdjustDown(int* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // 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 HeapSort1(int* a, int n)
{
	// 向下调整建堆 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}


void HeapSort2(int* a, int n)
{
	// 向下上调整建堆 O(N*logN)
	for (int i = 0; i < n; i++)
	{
		AdjustUp(a, n, i);
	}

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

}
int* sortArray(int* nums, int numsSize, int* returnSize) {
    (*returnSize) = numsSize;
    int* array = (int*)malloc(sizeof(int)*(*returnSize));
    for(int i = 0; i < numsSize; i++)
    {
        array[i] = nums[i];
    }
    HeapSort2(array, numsSize);
    return array;
}

结果分析: 

 

 可以看出无论是向上建堆还是向下建堆,堆排序都能够轻松通过

注意:
这里建议大家还是使用向下建堆实现堆排序,因为实现的时候只需要实现一个向下调整的算法,而且调整算法的时间复杂度为O(n),可谓是又方便,又高效.

2.3.6时间复杂度分析

堆排序的时间复杂度分析主要分为两个部分:建堆过程的时间复杂度和排序过程的时间复杂度。

1. 建堆过程的时间复杂度
--具体可以看这篇博客
数据结构之二叉树的超详细讲解(2)--(堆的概念和结构的实现,堆排序和堆排序的应用)-CSDN博客

建堆的过程是将一个无序的数组构建成一个堆,通常是最大堆或最小堆。建堆的时间复杂度为 O(n)

  • 建堆的详细步骤
    • 从最后一个非叶子节点开始,依次向前进行堆化操作,保证每个子树都满足堆的性质。
    • 最后一个非叶子节点的索引为 (n // 2) - 1,其中 n 是数组的长度。
    • 每次堆化的时间复杂度是 O(log n),因为堆的高度是 O(log n)

2. 排序过程的时间复杂度

排序过程主要包括将堆顶元素与末尾元素交换并重新调整堆的过程。排序过程的时间复杂度为 O(n log n)

  • 排序的详细步骤
    • 将堆顶元素与当前堆的最后一个元素交换,然后从根节点开始重新调整堆,使其重新满足堆的性质。
    • 每次重新调整堆的时间复杂度是 O(log n)
    • 总共需要进行 n-1 次交换和堆调整操作。

总结堆排序的时间复杂度:

  • 建堆时间复杂度:O(n)
  • 排序时间复杂度:O(n log n)

综合起来,堆排序的总时间复杂度是 O(n log n)。这使得堆排序在时间复杂度上表现出色,适合于大规模数据的排序操作。

3.直接选择排序与堆排序的比较

直接选择排序(Selection Sort)和堆排序(Heap Sort)都属于比较排序的算法,但它们在实现和性能上有显著的差异。

直接选择排序(Selection Sort)

  1. 基本原理

    • 直接选择排序通过每次从未排序的部分选出最小(或最大)的元素,然后放到已排序部分的末尾。
    • 每次选择过程都需要线性扫描未排序部分,找出最小元素,然后与当前位置交换。
  2. 时间复杂度

    • 最好情况、平均情况和最坏情况下的时间复杂度都是 O(n^2)
    • 每次选择都需要线性时间,总共进行了 n 次选择,每次选择需要线性时间。
  3. 空间复杂度

    • 额外空间复杂度是 O(1),因为只需要常数级别的额外空间用于交换元素。
  4. 稳定性

    • 直接选择排序是一种不稳定的排序算法,因为在选择的过程中可能破坏相同元素的相对顺序。

堆排序(Heap Sort)

  1. 基本原理

    • 堆排序利用堆的数据结构来实现排序。
    • 先构建一个最大堆(或最小堆),然后将堆顶元素(最大值或最小值)与堆的最后一个元素交换,并调整堆,使其满足堆的性质,重复这个过程直到整个数组排序完成。
  2. 时间复杂度:(向下调整建堆)

    • 建堆的时间复杂度是 O(n)
    • 每次调整堆的时间复杂度是 O(log n)
    • 总的时间复杂度是 O(n log n),堆排序是一种稳定的时间复杂度为 O(n log n) 的排序算法。
  3. 空间复杂度

    • 堆排序的空间复杂度是 O(1),因为所有操作都在原数组上进行,没有额外使用的空间。
  4. 稳定性

    • 堆排序在交换堆顶元素时可能破坏相同元素的相对顺序,因此是一种不稳定的排序算法。

总结比较

  • 性能:堆排序在大多数情况下比直接选择排序要快,特别是在处理大规模数据时,由于堆排序的时间复杂度为 O(n log n),比直接选择排序的 O(n^2) 要好。
  • 稳定性:直接选择排序可能比堆排序更容易实现为稳定排序,但通常它们都是不稳定的排序算法。
  • 实现复杂度:堆排序需要实现堆的构建和堆的调整过程,相对而言实现起来比直接选择排序稍微复杂一些,但在实际应用中通常都是可以接受的复杂度。

4.总结

直接选择排序的性能非常差,因为他每次遍历都需要完整遍历完整个数组,现实生活中基本不用,但堆排序时间复杂度能达到n*logn,特别是在top-k问题中,堆排序更是能到达O(n)的级别.

如果这篇文章对大家有帮助的话,麻烦点赞关注支持一下

我将会马上更新排序算法(3)之交换排序----冒泡排序和快速排序,记得点赞关注不迷路哦!

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

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

相关文章

【系统架构设计师】七、信息安全技术基础知识(信息安全的概念|信息安全系统的组成框架|信息加解密技术)

目录 一、信息安全的概念 1.1 信息安全的基本要素和范围 1.2 信息存储安全 1.3 网络安全 二、信息安全系统的组成框架 2.1 技术体系 2.2 组织机构体系 2.3 管理体系 三、 信息加解密技术 3.1 数据加密 3.2 对称加密技术 3.3 非对称加密算法 3.4 数字信封 3.5 信…

信息系统项目管理师(项目管理师)

项目管理者再坚持“聚焦于价值”原则时&#xff0c;应该关注的关键点包括&#xff1a;1价值是项目成功的最终指标&#xff1b;2价值可以再整个项目进行期间、项目结束或完成后实现&#xff1b;3价值可以从定性和/或定量的角度进行定义和衡量&#xff1b;4以成果为导向&#xff…

鸿蒙开发系统基础能力:【@ohos.pasteboard (剪贴板)】

剪贴板 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import pasteboard from ohos.pasteboard;属性 系统能力: 以下各项对应的系统能力均为SystemCapability.MiscServices.Pasteb…

ubuntu 18 虚拟机安装(1)

ubuntu 18 虚拟机安装 ubuntu 18.04.6 Ubuntu 18.04.6 LTS (Bionic Beaver) https://releases.ubuntu.com/bionic/ 参考&#xff1a; 设置固定IP地址 https://blog.csdn.net/wowocpp/article/details/126160428 https://www.jianshu.com/p/1d133c0dec9d ubuntu-18.04.6-l…

相亲交友微信小程序系统源码

开启浪漫邂逅新篇章 相亲交友——随着年龄的增长&#xff0c;越来越多的人开始关注自己的婚姻问题&#xff0c;为了提高相亲服务的质量&#xff0c;这款应用就可以拓宽在线社交网络范围。​ &#x1f491; 引言&#xff1a;邂逅爱情的新方式 在繁忙的都市生活中&#xff0c;寻…

《梦醒蝶飞:释放Excel函数与公式的力量》6.1 DATE函数

6.1 DATE函数 第一节&#xff1a;DATE函数 1&#xff09;DATE函数概述 DATE函数是Excel中的一个内置函数&#xff0c;用于根据指定的年、月、日返回对应的日期序列号。这个函数非常有用&#xff0c;尤其是在处理日期数据时&#xff0c;它可以帮助你构建特定的日期&#xff0…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及氢储能与需求响应的路域综合能源系统规划方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

DOOPRIME:美联储降息何时到来?经济数据是关键

摘要&#xff1a; 美联储理事库克在纽约经济俱乐部的演讲中表示&#xff0c;降息时机取决于未来的经济数据和通胀走势。她预期&#xff0c;明年通胀将显著放缓&#xff0c;其中核心商品通胀可能保持轻微负增长&#xff0c;剔除住房的核心服务通胀将逐步放缓。尽管市场对降息时…

易查分小程序丨查询下拉选择如何设置?

老师在发布查询时&#xff0c;如果查询条件过长或难以记忆&#xff0c;学生家长经常输入不正确&#xff0c;或难以记住&#xff0c;应该怎样解决&#xff1f; 可以使用【查询条件下拉选择功能】&#xff0c;让学生家长在下拉框中直接选择查询条件。 查询下拉演示效果 &#x1f…

宠物领养救助管理系带万字文档java项目基于springboot+vue的宠物管理系统java课程设计java毕业设计

文章目录 宠物领养救助管理系统一、项目演示二、项目介绍三、万字项目文档四、部分功能截图五、部分代码展示六、底部获取项目源码带万字文档&#xff08;9.9&#xffe5;带走&#xff09; 宠物领养救助管理系统 一、项目演示 宠物领养救助系统 二、项目介绍 基于springbootv…

代码随想录——K 次取反后最大化的数组和(Leetcode1005)

题目链接 贪心 class Solution {public int largestSumAfterKNegations(int[] nums, int k) {int sum 0;Arrays.sort(nums);// 先把负数转正for(int i 0; i < nums.length; i){if(nums[i] < 0 && k > 0){nums[i] -nums[i];k--;}sum nums[i];}Arrays.so…

springcloud-sentinel 限流组件中文文档

快速开始 欢迎来到 Sentinel 的世界&#xff01;这篇新手指南将指引您快速入门 Sentinel。 Sentinel 的使用可以分为两个部分: 核心库&#xff08;Java 客户端&#xff09;&#xff1a;不依赖任何框架/库&#xff0c;能够运行于 Java 8 及以上的版本的运行时环境&#xff0c…

C++编程逻辑讲解step by step:把一个整数按位拆分成1位整数。

分析 步骤之间有何关联&#xff1f; 思考的逻辑&#xff1a; 第一步得到4&#xff0c;ba%10;即可 第二步得到3&#xff0c;但需要先得到123&#xff0c;所以要除10&#xff0c;即c(a/10) %10; 第三步得到2&#xff0c;但需要先得到12 &#xff0c;所以要除100&#xff0c…

[XYCTF新生赛2024]-PWN:ptmalloc2 it‘s myheap plus解析(glibc2.35,堆中的栈迁移,orw)

查看保护 查看ida 思路&#xff1a; 泄露libc和堆地址就不多说了&#xff0c;fastbin duf也不解释了。这里主要是利用fastbin duf在environ附近创建堆块&#xff0c;泄露environ中的栈地址&#xff0c;然后就利用fastbin duf修改rbp和返回地址进行栈迁移了&#xff0c;迁移目标…

日志打印中对容器(包括多级容器)的通用输出

在日志打印中&#xff0c;往往有打印一个数组、集合等容器中的每个元素的需求&#xff0c;这些容器甚至可能嵌套起来&#xff0c;如果每个地方都用for循环打印&#xff0c;将会特别麻烦。基于这种需求&#xff0c;作者尝试实现一个通用的打印函数SeqToStr()&#xff0c;将容器序…

47岁TVB儿童节目主持20多年美貌零走样

现年47岁的香港艺人张洁莲&#xff08;Jackeline姐姐&#xff09;自从2020年嫁给拍拖多年的TVB「不老型男」袁文杰后&#xff0c;不时都在社交平台分享合照&#xff0c;夫妻间甜蜜恩爱&#xff0c;相信正是她多年来的保养秘诀。 最近有网友在社交平台分享了与张洁莲的合照&…

docker配置redis主从复制

下载redis,复制redis.conf 主节点(6379) 修改redis.conf # bind 127.0.0.1 # 注释掉这里 protected-mode no # 改为no port 6379从节点(6380) 修改redis.conf bind 127.0.0.1 protected-mode no # 改为no port 6380 replicaof 172.17.0.2 6379 # 这里的ip为主节点容器的i…

CS与MSF的权限互相传递/mimikatz抓取windows 2012明文密码

目录 CS和MSF的简单介绍 Metasploit Cobalt Strike 1、CS权限传递到MSF 2、MSF权限传递到CS 3、使用mimikatz抓取明文密码 通过修改注册表用户重新登录后抓取明文密码 今天的任务是两个 一个是CS与MSF的权限互相传递一个是抓取windows2012的明文密码 那就分别来完成 …

结构冒险,控制冒险,数据冒险实例分析

目录 1.结构冒险&#xff1a; 2.数据冒险&#xff1a; 3.控制冒险&#xff1a; 指令执行过程&#xff1a; 取指(IF)&#xff1a;从指令存储器或 Cache 中取指令。 译码/读寄存器(ID)&#xff1a;操作控制器对指令进行译码&#xff0c;同时从寄存器堆中取操作数。 执行/计算地…

文字实录|Checkout.com大中华区总经理项尧:品牌全球化发展中的支付运营策略

大家好&#xff0c;很高兴在此次【品牌全球化营销增长峰会】与大家一起分享和交流。 我叫项尧&#xff0c;是 Checkout.com 大中华区的总经理&#xff0c;在支付领域有将近15年的经验。 我们 Checkout.com 是一家总部位于英国的支付公司&#xff0c;专注于线上收单&#xff0…