简单记录牛客top101算法题初级题(C语言实现)BM12 单链表的排序

news2024/11/26 5:35:44

1. BM12 单链表的排序

  要求:给定一个节点数为n的无序单链表,对其按升序排序。
         在这里插入图片描述

输入:[1,3,2,4,5]
返回值:{1,2,3,4,5}

1.1 自己的整体思路

  1. 开始的时候使用冒泡排序,但是冒泡排序的时间复杂度是O(n^2),空间复杂度是O(1),但是时间复杂度超了。归并排序的空间复杂度是O(n),时间复杂度是O(nlogn),这里也就是用了归并排序的思想。
  2. 一直从链表的中间分割链表(用到了链表中的快慢指针,快指针、慢指针同时走,快指针走到链表尾部,慢指针指向链表的中点位置),直到每个子链表只包含一个元素为止。
  3. 依次拼接分割后的链表(各个单节点元素的大小进行比较后),形成了一个新的链表,就完成了排序。
// 合并两个有序链表
struct ListNode* merge(struct ListNode* left, struct ListNode* right) {
    struct ListNode dummy;            //栈上存储 ,也可以动态创建,堆上存储   
 /*
 //堆上存储
  struct ListNode *newNode = (struct ListNode *)malloc(sizeof(struct ListNode));      //增加一个新的结点
  newNode->next = NULL;                                                               //新结点指向NULL
  struct ListNode *newNode1 = newNode;                                                //定义一个指针指向该头结点
*/
    struct ListNode* tail = &dummy;
    while (left && right) {
        if(left->val < right->val){
            tail->next = left;
            left = left->next;
        } else {
            tail->next = right;
            right = right->next;
        }
        tail = tail->next;
    }
    tail->next = left ? left : right;  //将归并排序过程中左侧链表和右侧链表合并成一个有序链表。如果left不为空,则返回left,否则返回right。因为链表长短,去掉公共的,长的就继续接上。
    return dummy.next;
}
// 归并排序函数
struct ListNode* sortInList(struct ListNode* head) {
    if (!head || !head->next) {   //链表为空,或者链表只有一个元素
        return head;
    } 
    // 使用快慢指针找到链表的中点
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while (fast->next && fast->next->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    struct ListNode* left = head;        //头结点成为左结点
    struct ListNode* right = slow->next; //慢指针(中间指针)的下一个指针赋值给右指针
    slow->next = NULL;                   //断开链表指针,中间指针指向NULL
    left = sortInList(left);             //递归
    right = sortInList(right);           //递归
    return merge(left, right);           //拼接字符串
}

举例说明:

//以这个链表为例
1 -> 2 -> 3 -> 4 -> 5

1.sortInList 开始排序整个链表,分为左侧 left 和右侧 right。
 left:4 -> 2 -> 1
 right:3 -> 5
2.对 left 递归排序,分解为:
 left_left:4
 left_right:2 -> 1
3.继续对 left_right 递归排序,分解为:
 left_right_left:2
 left_right_right:1
4.合并 left_right_left 和 left_right_right,得到 left_right:1 -> 2
5.继续递归排序 left_left,得到 left_left:4
6.对 right 递归排序,分解为:
 right_left:3
 right_right:5
7.合并 left_left 和 left_right,得到 left:1 -> 2 -> 4
8.合并 right_left 和 right_right,得到 right:3 -> 5
9.最后,合并 left 和 right,得到最终的排序链表:1 -> 2 -> 3 -> 4 -> 5

1.2 其他的方法(大佬方法)

//使用快速排序
//快速排序中使用了双指针,对于链表,不是很好操作,这里把链表转成数组,把数组排好序后,再转成链表

int patition(int* arr, int low, int high){
    int temp = arr[low];              //选择基准元素
    while(low < high){
        while(low < high && arr[high] >= temp){   //检测右边是否都大于基准元素
            high--;
        }
        arr[low] = arr[high];                     //把小于基准元素的值放到起始位置
        while(low < high && arr[low] <= temp){
            low ++;
        }
        arr[high] = arr[low];                    //把大于基准元素的值放到又边位置
    }
     
    arr[low] = temp;                             //low = high时候,放上基准元素
    return low;                                  //返回索引,完成一次排序
}
void quickSort(int *a, int low, int high){
    if(low < high){
        int loc = patition(a, low, high);         //loc是基准索引位置
        quickSort(a, low, loc - 1);  			  //递归基准索引前段
        quickSort(a, loc + 1, high);              //递归基准索引后段
    }
}
struct ListNode* sortInList(struct ListNode* head ) {
    if(head == NULL || head ->next == NULL){    //如果为空,或者只有一个元素,返回自身
        return head;
    }
    int count = 0;
    struct ListNode* p = (struct ListNode*)malloc(sizeof(struct ListNode));  //创建一个新指针
    p = head;                                    //指针指向头结点
    while(p != NULL){                            //计算链表的长度  
        count++;                 
        p = p->next;
    }
    int* arr = (int*)malloc(sizeof(int)*count);  //申请和链表长度一样的数组
    p = head;                                    //指针回到头指针
    int i = 0;
    while(p!=NULL){   
        arr[i] = p->val;                         //把链表的值赋值给数组
        p = p->next;
        i++;
    }
    quickSort(arr, 0, count - 1);               //调用快速排序
    p = head;
    for(i = 0; i < count; i++){                 //依次替换链表中的值,使得链表有序
        p->val = arr[i];
        p = p->next;
    }
    return head;
}

1.3 小结

1.3.1 各种简单常用排序方法

1.3.1.1 冒泡排序

  简介:冒泡排序(Bubble Sort)是一种简单的排序算法,其基本思想是从列表的一端开始,依次比较相邻的两个元素,如果它们的顺序不正确就交换它们,然后继续向列表的另一端移动,重复这个过程,直到整个列表变得有序为止。
  在最坏情况下,冒泡排序需要进行 n-1 轮比较和交换,其中 n 是待排序元素的数量。在每一轮中,需要比较相邻的元素并进行交换。所以,在最坏情况下,总的比较次数为 (n-1) + (n-2) + … + 1 = n * (n-1) / 2。因此,冒泡排序的时间复杂度为O(n^2)
  冒泡排序的空间复杂度主要取决于交换元素时所使用的临时变量。在每次交换过程中,只需要一个临时变量来存储一个元素的值,因此空间复杂度为O(1)
  举例说明:

#include <stdio.h>
#include <string.h>
int main()
{
   /*  Write C code in this online editor and run it. */
int arr[5] = {2,7,3,8,7};
int n = sizeof(arr)/sizeof(arr[0]);         //计算数组的长度,strlen是计算字符串的长度,不是数组
for(int i = 0; i < n; i++){                 //i是控制循环的次数
    for(int j = 0; j < n - i - 1; j++){     //下面有j+1,这里的j控制前一个指针
       if(arr[j] > arr[j + 1]){
       int temp = arr[j + 1];
       arr[j + 1] = arr[j];
       arr[j] = temp;
    }
  }
}
for(int k = 0;k < n;k++){
      printf("val = %d\r\n",arr[k]);
}
   return 0;
}

在这里插入图片描述

1.3.1.2 选择排序

  简介:选择排序(Selection Sort)是一种简单的排序算法,它的基本思想是在未排序的部分中找到最小(或最大)的元素,然后将其放到已排序部分的末尾。重复这个过程,直到整个数组都被排序。
  时间复杂度:选择排序的时间复杂度是 O(n^2),其中 n 是元素的数量。
  空间复杂度:选择排序的空间复杂度是 O(1),因为它仅需要一个常数级别的额外空间来存储临时变量。

#include <stdio.h>
#include <string.h>
int main()
{
   /*选择排序*/
int arr[5] = {2,7,3,8,7};
int n = sizeof(arr)/sizeof(arr[0]);         //计算数组的长度,strlen是计算字符串的长度,不是数组
	
for(int i = 0; i < n; i++){                 //i是控制循环的次数
    for(int j = i + 1; j < n; j++){         //j这里也是小于n,不是n - 1,n - 1导致最后一个没有排序
       if(arr[i] > arr[j]){
       int temp = arr[j];
       arr[j] = arr[i];
       arr[i] = temp;
    }
  }
}
for(int k = 0;k <n;k++){
      printf("val = %d\r\n",arr[k]);
}
   return 0;
}

运行结果如下:
在这里插入图片描述

1.3.1.3 快速排序

  简介:快速排序(Quick Sort)是一种高效的、基于分治策略的排序算法。快速排序的核心思想是选择一个基准元素,将数组分成两个子数组,小于基准的元素放在左边,大于基准的元素放在右边,然后对这两个子数组分别进行递归排序。
  快速排序的时间复杂度为O(nlog n),其中n是待排序元素的数量。
  快速排序的空间复杂度为O(log n),其中n是待排序元素的数量。这是因为快速排序通常使用递归来进行分区,每次递归调用都会消耗一些栈空间。因此,递归的深度通常为O(log n)。这使得快速排序对于内存的消耗较低。
步骤:

  1. 选择基准元素: 从待排序的数组中选择一个元素作为基准元素。通常选择第一个元素、最后一个元素或中间元素作为基准。
  2. 分区(Partition): 将数组中的元素分成两部分,使得左边的元素都小于或等于基准元素,右边的元素都大于基准元素。分区过程可以使用多种方法,常见的是使用两个指针从数组的两端开始,向中间移动,交换不符合要求的元素,直到两个指针相遇。
  3. 递归排序: 递归地对分区后的两个子数组进行快速排序。分别对左边和右边的子数组重复上述两个步骤,直到子数组的大小为0或1,此时它们都被认为是有序的。
  4. 合并结果: 将排序后的子数组合并在一起,得到最终的有序数组。
      举例说明:
#include <stdio.h>
#include <string.h>
//递归函数
// 将数组分区并返回分区点的索引
void quickSort(int *arr, int low, int high){
	if(low >= high){        //递归结束的条件
		return ;
	}
 // if(low < high){
	int pivot = arr[low];              //选择数组的第一个元素作为基准,以第一个元素作为基准,那就以最后一个元素开始比较
	int left = low;                    //左索引,看值是否要大于pvoit,放到右边
	int right = high;                  //右索引,看值是否要小于pvoit,放到左边

	while(left < right){
		while( arr[right] > pivot){
			right--;		
		}
		while( arr[left] <= pivot){
			left++;		
		}
		if(left < right){
			int temp =  arr[right];
			arr[right] = arr[left];
			arr[left] = temp;
		}
	}
	//它们的作用是确保基准元素被放置在分区后的正确位置,以确保左侧的元素都小于等于基准元素,右侧的元素都大于基准元素。
	arr[low] = arr[right];   //把首位置值给最后索引到的值
	arr[right] = pivot;      //把pivot放到正确的位置,就是两个指针重合的位置,arr[right] = pivot;
	
	quickSort(arr,low,right - 1);  //是right,因为最后执行了right--;结束了循环,这里不是left了
	quickSort(arr,right + 1,high);
// }	
}

int main()
{
/*快速排序*/
int arr[7] = {2,7,3,8,7,1,9};
int n = sizeof(arr)/sizeof(arr[0]);    //计算数组的长度,strlen是计算字符串的长度,不是数组
quickSort(arr,0,n - 1);                //这边传入的是n - 1,不是n

for(int k = 0;k <n;k++){
      printf("val = %d\r\n",arr[k]);
}
   return 0;
}

运行结果如下:
在这里插入图片描述
初始数组:2, 7, 3, 8, 7, 1, 9
1.quickSort(arr, 0, 6) - 调用 quickSort 函数,对整个数组进行排序。
2.第一次递归:
 基准元素 pivot 选择为 2(第一个元素)。
 左指针 left 初始化为 0,右指针 right 初始化为 6。
 进入循环,right 向左移动,直到找到小于等于 pivot 的元素为止,即 arr[5](值为 1)。
 left 向右移动,直到找到大于 pivot 的元素为止,即 arr[1](值为 7)。
 交换 arr[1] 和 arr[5],现在数组变为 2, 1, 3, 8, 7, 7, 9。
 左指针 left 和右指针 right 继续移动,直到left的索引是2,就是值为3的时候,right的索引是1,值是1的时候,停止循环。
 将 pivot(值为 2)放在正确的位置,把right的位上值放到首位置,基准元素放到right上,数组变为 1, 2, 3, 8, 7, 7, 9。
对于 quickSort(arr, 0, 0),因为只有一个元素,递归结束,左侧子数组变为 1。
对于 quickSort(arr, 2, 6),继续进行递归。3,8,7,7,9。和上面循环一样,不在赘述。

1.3.1.4 归并排序

  归并排序(Merge Sort)是一种常见的排序算法,它采用分而治之(Divide and Conquer)的策略来排序数组或列表元素。下面是归并排序的主要步骤:
  分割(Divide):将原始数组或列表分成两个较小的子数组(或子列表),每个子数组包含原始数据的一半。这个步骤递归地继续,直到每个子数组只包含一个元素为止。
  合并(Merge):将两个子数组或子列表合并为一个新的有序数组或列表。这是通过逐个比较两个子数组(或子列表)的元素并将其按顺序放入新的数组(或列表)中来完成的。这个过程一直持续到所有元素都被合并到一个有序数组(或列表)中。
  递归:重复上述步骤,直到整个数组(或列表)已经被合并成一个有序序列。
  在任何情况下,归并排序的时间复杂度都是O(nlog n)。在经典的归并排序实现中,通常需要O(n)的额外空间来存储临时数组,用于合并过程。此外,递归版本的归并排序还需要O(log n)的栈空间,因为递归调用栈的深度最多为log n层。一般认为归并排序的时间复杂度是O(n log n),而空间复杂度是O(n)。
  举例说明:

#include <stdio.h>

void merge(int *arr,int *left,int *right,int left_len,int right_len){
	int i = 0;
	int j = 0;	
	int k = 0;	
	
	while(i < left_len && j < right_len){
		if(left[i] <= right[j]){
			 arr[k] = left[i];
	      	 k++;
		     i++;
		}else{
		     arr[k] = right[j];
	      	 k++;
		     j++;
		}
	}		
		while(i < left_len){
			 arr[k] = left[i];
	      	 k++;
		     i++;
		}
		while(j < right_len){
             arr[k] = right[j];
	      	 k++;
		     j++;		
		}
}
void mergeSort(int *arr,int n){
	if(n <= 1){
		return ; //已经有序或者为空,无需排序
	}
	int mid = n / 2;
	int left[mid];      //声明一个数组,它在栈上分配了mid个整数的内存空间,当该函数或代码块执行完毕时,该数组会自动被销毁,释放所占用的内存
	int right[n - mid]; //声明一个数组
	//分割数组
	for(int i = 0;i < mid;i++){
		left[i] = arr[i];  //左子数组接收左边数据
	}
	for(int i = mid;i < n;i++){
		right[i - mid] = arr[i];  //右子数组接收左边数据
	}
	mergeSort(left,mid);       //分割左子数组
	mergeSort(right,n - mid);  //分割右子数组
	merge(arr,left,right,mid,n - mid);  //排序
}
int main()
{
   /*归并排序*/
	int arr[5] = {2,7,3,8,1};
	int n = sizeof(arr)/sizeof(arr[0]);
	//调用归并排序
    mergeSort(arr, n);
	 for (int i = 0; i < n; i++) {
        printf("val = %d\r\n ", arr[i]);
    }
   return 0;
}

在这里插入图片描述
  具体的程序运行和上面的链表的差不多,不再赘述。

1.3.1.5 堆排序

  堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法,它具有稳定的时间复杂度O(n log n),并且不需要额外的辅助存储空间。以下是堆排序的基本步骤:

  建立最大堆(Build Max Heap):首先,将待排序的数组视为一个二叉堆(通常是一个数组的形式),并从最后一个非叶子节点开始,逐步将数组调整为最大堆。最大堆是一种二叉树,其中每个节点的值都大于或等于其子节点的值。

  堆排序(Heapify):将最大堆的根节点(即数组的第一个元素)与堆的最后一个元素交换。然后,减小堆的大小并重新调整堆,以确保新的根节点是剩余元素中的最大值。重复这个过程,直到整个数组有序。

  整个过程的思路是,从堆中不断选择最大的元素,将其放在数组的末尾,然后将剩余的元素重新构建为最大堆,重复这个过程直到整个数组都排好序。这就是堆排序的核心思想。

#include <stdio.h>

//堆化函数:将根节点为i的子树调整为最大堆
void maxHeapify(int *arr, int n, int i){   //n是堆的大小,即数组的长度。i是要调整为最大堆的子树的根节点索引
	int largest = i;        //初始化最大元素为根节点
	int left = 2 * i + 1;   // 左子节点的索引
	int right = 2 * i + 2;  // 右子节点的索引
	
	// 如果左子节点存在且大于根节点,则更新最大元素索引
    if (left < n && arr[left] > arr[largest]) {  
        largest = left;         //这是给的索引
    }
	
	// 如果右子节点存在且大于根节点,则更新最大元素索引 ,如果不存在就不比较,确实要判断
    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }
	    // 如果最大元素索引不是根节点索引,则交换根节点和最大元素
    if (largest != i){
		int temp = arr[largest];
		arr[largest] = arr[i];
		arr[i] = temp;

        //递归调用堆化函数
        maxHeapify(arr, n, largest);
    }						  
 }
// 堆排序函数
void heapSort(int arr[], int n) {
    // 建立最大堆(从最后一个非叶子节点开始)考虑一个完全二叉树(或者说是近似完全二叉树),在这种树中,大约一半的节点是叶子节点,而另一半是具有子节点的非叶子节点。如果我们从数组表示的堆的最后一个非叶子节点开始,然后逐个向前调用 maxHeapify 函数,我们可以有效地确保整个数组构成的堆在初始堆化过程中满足最大堆性质。 记住是从n / 2 - 1处开始的。
    for (int i = n / 2 - 1; i >= 0; i--) {
        maxHeapify(arr, n, i);
    }
    // 依次将最大元素(根节点)与堆的最后一个元素交换,然后堆化
    for (int i = n - 1; i > 0; i--) {
		int temp = arr[i];
		arr[i] = arr[0];
		arr[0] = temp;
        maxHeapify(arr, i, 0);
    }
}
int main()
{
	/*  堆排序  */
	int arr[6] = {12, 11, 13, 5, 6, 7};
    int n = sizeof(arr) / sizeof(arr[0]);
	heapSort(arr, n);
    printf("排序后的数组: \n");
    for (int i = 0; i < n; i++) {
        printf("val = %d\r\n ", arr[i]);
    }
   return 0;
}

在这里插入图片描述
 上述代码举例说明:
 初始的数组是12, 11, 13, 5, 6, 7。
 构建大根堆,堆中的每个节点的值都大于或等于其子节点的值。这意味着堆的根节点始终包含堆中的最大元素。
在这里插入图片描述第一次交换首位元素,并大根堆化:
在这里插入图片描述
第二次交换首位元素,并大根堆化:
在这里插入图片描述
第三次交换首位元素,并大根堆化:
在这里插入图片描述
第四次交换首位元素,并大根堆化,此时i = 2 ,left = 1,right = 2,不再与右边的值交换:
在这里插入图片描述
第五次交换首位元素,并大根堆化,此时i = 1 ,left = 1,right = 2,不再与左边和右边的值交换(也就是最后一次交换):
             在这里插入图片描述
最后的排序结果就是:5, 6, 7, 11,12, 13。

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

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

相关文章

Day52|leetcode 300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

leetcode 300.最长递增子序列 题目链接&#xff1a;300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划之子序列问题&#xff0c;元素不连续&#xff01;| LeetCode&#xff1a;300.最长递增子序列_哔哩哔哩_bilibili 题目概述 给你一…

政企局域网办公首选:WorkPlus专为政企打造的IM即时通讯平台

政府机构与企业在信息交流与协作中的安全高效需求&#xff0c;使得私有化部署的IM即时通讯平台成为必要选择。WorkPlus提供符合客户应用场景的数字化平台解决方案&#xff0c;满足政企局域网办公需求。WorkPlus如何为政企提供定制化的IM即时通讯解决方案&#xff0c;助力政企机…

【论文绘图】seaborn分类数据绘图

参考&#xff1a;https://seaborn.pydata.org/tutorial/categorical.html 分类变量关系图中的catplot类似于连续变量中的relplot&#xff0c;默认是stripplot。 分类变量图种类 分类散点图 stripplotswarmplot (kind‘swarm’) 类别分布图 boxplotviolinplotboxenplot …

Chrome实现自动化测试:录制回放网页动作

Chrome 浏览器是真的恐怖&#xff0c;它会把相关的小工具都卷死。从它诞生至今&#xff0c;创造了一个又一个的传奇&#xff0c;现在可以看到基于它的操作系统 chrome os &#xff0c;还能买到用它做系统的笔记本电脑。 最近&#xff0c;新版本支持录制和回放功能了。有了这个…

算法通关村14关 | 堆结构

1. 堆的概念与特征 堆是将一组数据按照完全二叉树的存储顺序&#xff0c;将数据存储在一维数组中的结构&#xff0c;对的结构有两种&#xff0c;一种称为大顶堆&#xff0c;一种称为小顶堆。 小顶堆&#xff1a;任意节点的值均小于等于它的左右孩子&#xff0c;并且最小的值位于…

Matlab(GUI程式设计)

目录 1.MatlabGUI 1.1 坐标区普通按钮 1.1.1 对齐组件 1.1.2 按钮属性 1.1.3 脚本说明 1.1.4 选择呈现 1.3 编译GUI程序 在以前的时候&#xff0c;我们的电脑还是这样的 随着科技的不断进步&#xff0c;我们的电脑也发生着翻天覆地的改变1990s&#xff1a; 在未来&#xff0c…

(三)行为模式:7、观察者模式(Observer Pattern)(C++示例)

目录 1、观察者模式&#xff08;Observer Pattern&#xff09;含义 2、观察者模式的UML图学习 3、观察者模式的应用场景 4、观察者模式的优缺点 &#xff08;1&#xff09;优点&#xff1a; &#xff08;2&#xff09;缺点 5、C实现观察者模式的实例 1、观察者模式&…

DP读书:鲲鹏处理器 架构与编程(十一)鲲鹏生态软件架构 AND 硬件特定软件

鲲鹏生态软硬件构成 鲲鹏软件构成硬件特定软件1. Boot Loader2. SBSA 与 SBBR3. UEFI4. ACPI 鲲鹏软件构成 鲲鹏处理器的软件生态是一个不断发展的软件生态&#xff0c;服务器本身也具有复杂度多样性&#xff0c;经过很长时间的发展服务器硬件有不同的操作系统方案&#xff0c…

创造商业新机遇,短视频商城APP崭露头角

随着移动互联网和社交媒体的飞速发展&#xff0c;短视频平台逐渐成为人们获取信息和娱乐的重要渠道。基于此&#xff0c;打造短视频商城APP成为引爆颠覆式购物新潮的利器。短视频商城APP结合了短视频分享和电子商务的优势&#xff0c;为用户提供了一种全新的购物体验。 颠覆传统…

AI 模型:数据收集和清洗

为了训练AI模型,需要收集和准备足够的数据。数据应该涵盖各种情况和场景,以确保系统在各种情况下都能准确地运行。数据原始来源应该是真实的,并且应该涵盖系统预计的使用情况。数据应该根据特定的需求进行采样和处理,可以来自各种来源,例如公共数据集、第三方数据提供商、…

在项目管理中,如何做好进度规划?这两点很重要!

生活中&#xff0c;做事前做好计划&#xff0c;结果总不会太差。如果是走哪算哪&#xff0c;到最后可能什么也做不好。日常生活中尚且如此&#xff0c;在项目管理中涉及人员、任务多&#xff0c;所以&#xff0c;项目经理必须具备规划能力&#xff0c;统筹项目的各种组织和要素…

网络安全(黑客)自学笔记学习路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…

Spring Boot业务代码中使用声明式事务@Transactional失效踩坑点总结

&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;业务代码踩坑总结 &#x1f468;‍&#x1f4bb;本文简述&#xff1a;Spring Boot业务代码中使用声明式事务Transactional失效踩坑点总结 &#x1f468;‍&#x1f4bb;上一篇文章&#xff1a; &#x1f468;‍&#x1f4bb;有…

Leetcode394 字符串解码

思路&#xff1a;类似于入栈出栈的操作&#xff0c;分层保存数字和字符串&#xff0c;然后逐层相乘合并&#xff0c;通过判断当前字符和上一个字符类型来确定数字是否结束 class Solution:def decodeString(self, s: str) -> str:current_str [""]current_num[]…

第二章 局部图像描述子

文章目录 第二章 局部图像描述子2.1Harris角点检测器2.2SIFT&#xff08;尺度不变特征变换&#xff09;2.2.1兴趣点2.2.2描述子2.2.3检测兴趣点 第二章 局部图像描述子 本章旨在寻找图像间的对应点和对应区域。本章将介绍用于图像匹配的两种局部描述子算法。本书的很多内容中都…

RocketMQ 安装与入门

文章目录 简介下载下载目录地址 安装部署环境要求下载二进制包解压即可启动 NameServer 启动BrokerProxy单组节点单副本模式启动 使用Java客户端发布订阅消息1. 创建主题 topic2. 创建Java工程使用Maven引入Java SDK包生产者代码 ProducerDemo消费者代码 ConsumerDemo RocketMQ…

SPSS教程:如何绘制带误差的折线图

SPSS教程&#xff1a;如何绘制带误差的折线图 1、问题与数据 研究者想研究45-65岁健康男性中&#xff0c;静坐时长和血胆固醇水平的关系&#xff0c;故招募100名研究对象询问其每天静坐时长&#xff08;time&#xff09;&#xff0c;并检测其血液中胆固醇水平&#xff08;cho…

VSCode下载、安装及配置、调试的一些过程理解

第一步先下载了vscode&#xff0c;官方地址为&#xff1a;https://code.visualstudio.com/Download 第二步安装vscode&#xff0c;安装环境是win10&#xff0c;安装基本上就是一步步默认即可。 第三步汉化vscode&#xff0c;这一步就是去扩展插件里面下载一个中文插件即可&am…

安全测试-django防御安全策略

django安全性 django针对安全方面有一些处理&#xff0c;学习如何进行处理设置&#xff0c;也有利于学习安全测试知识。 CSRF 跨站点请求伪造&#xff08;Cross-Site Request Forgery&#xff0c;CSRF&#xff09;是一种网络攻击方式&#xff0c;攻击者欺骗用户在自己访问的网…

【实验二】基尔霍夫定律和叠加定理

【实验内容】 【实验报告】 表一线路图 表二线路图 线路1 线路2 同时作用 【得分】