【数据结构】详解线性表的各个函数接口+OJ习题讲解(画图比写代码更重要!!!)

文章目录

    • 一、线性表
    • 二、顺序表
      • 1、什么是顺序表
      • 2、顺序表的分类
    • 三、动态顺序表的实现
      • 1、结构的定义
      • 2、顺序表的初始化
      • 3、检查容量
      • 4、在头部插入数据
      • 5、在尾部插入数据
      • 6、在指定位置插入数据
      • 7、在尾部删除数据
      • 8、在头部删除数据
      • 9、删除指定位置的数据
      • 10、查找数据
      • 11、修改指定位置的数据
      • 12、打印顺序表中的数据
      • 13、顺序表的销毁
    • 四、完整代码
      • 1、SeqLIst.h
      • 2、SeqList.c
      • 3、test.c
    • 五、顺序表的缺陷
    • 六、顺序表力扣OJ题
      • 1、移除元素
        • 思路1
        • 思路2
        • 思路3
      • 2、删除有序数组中的重复项
      • 3、合并两个有序数组

一、线性表

是什么线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列; 线性表是一种在实际中广泛使 用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串…

线性表的结构

线性表在逻辑上是线性结构,也就说是连续的一条直线;但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

本篇文章,我们先讲解数组形式存储结构存储的线性表!
image-20220728153107491


二、顺序表

1、什么是顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。

简单来说,顺序表就是数组,只是要求数组里面的元素必须连续存储而已。

2、顺序表的分类

顺序一般分为两类:静态顺序表和动态顺序表。

静态顺序表:采用定长数组来存储元素。(一般不考虑)

#define MAX 1000 //数组的最大长度 typedef int SLDtataType; //重命名数据类型 typedef struct SeqList { SLDtataType data[MAX]; //使用定长数组来存储数据 size_t size; //有效数据的个数 }SL;

动态顺序表:使用动态开辟的数据来存储元素。(重点 有三个元素组成)

typedef int SLDataType; //将数据类型重命名为SLDataType 
typedef struct SeqList { 
	SLDataType* data; //对应数据类型的指针,用来指向动态开辟的空间 
	size_t size; //记录当前有效数据的个数 
	size_t capacity; //记录当前容量,不够就增容 
}SL;

两种顺序表的对比:相较于动态顺序表,静态顺序表存在很大的缺陷,那就是空间问题:当我们数据量很大时给定的空间可能不够用,但我们数据量比较小时,给定的空间又可能过大,造成空间浪费,即静态顺序表只适用于确定知道需要存多少数据的场景;所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,而静态顺序表很少使用。下面我们用C语言来模拟实现一个动态的顺序表。


三、动态顺序表的实现

1、结构的定义

#define DEF_SIZE 5 //初始容量 
#define CRE_SIZE 2 //一次扩容的倍数 
typedef int SLDataType; //将数据类型重命名为SLDataType 
typedef struct SeqList { 
	SLDataType* data; //对应数据类型的指针,用来指向动态开辟的空间 
	size_t size; //记录当前有效数据的个数 
	size_t capacity; //记录当前容量,不够就增容 
}SL;

如上:我们将要管理的数据类型重命名为SLDateType,这样以后当我们要用此顺序表管理其他数据类型时,我们就只需要改动这一个地方。

其次,相较于静态顺序表,我们的结构体多了一个参数 – capacity,我们用它来记录顺序表当前的容量,当当前的有效数据个数size与它相等时,我们就进行扩容;由于数据个数和顺序表的容量都不可能小于0,所以我们将其定义为size_t的。

最后就是关于初始容量和每次扩增倍数的问题,这里把初始容量设定为5,然后把扩增倍数设定为2,即我们的顺序表每次扩容两倍。

2、顺序表的初始化

在初始化函数中,我们把size和capacity都置为相应大小,并且为data指针动态开辟一块空间,用于存储数据。

void SLInit(SL* psl)
{
	assert(psl);

	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

3、检查容量

在检查容量的函数中,当我们结构体中的size和capacity相等时,我们就扩容,在扩容时我们要注意不要直接用data指针来接收realloc函数的返回值,避免扩容失败导致data指针找不到之前管理的空间,从而造成内存泄漏。

void SLCheckCapacity(SL* psl)
{
	// 检查容量
	if (psl->size == psl->capacity)
	{
		int newCapcity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, newCapcity*sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
			//exit(-1);
		}

		psl->a = tmp;
		psl->capacity = newCapcity;
	}

}

这里realloc有一个知识点:
在这里插入图片描述

在这里插入图片描述

我们realloc 后面写的是个数*sizeof(SLDatatype) 而不是单纯的个数 因为我们开辟的是空间大小

4、在头部插入数据

在头部插入数据时,我们需要先将顺序表中的数据整体向后挪动一位,然后在顺序表的开头插入;在插入完成后记得要让size++。

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);

	// 挪动数据
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}
	psl->a[0] = x;
	psl->size++;
}

5、在尾部插入数据

在尾部插入数据很简单,直接插入就行。

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);

	SLCheckCapacity(psl);

	psl->a[psl->size] = x;
	psl->size++;
}

6、在指定位置插入数据

在此函数中,我们需要先将pos及其之后的元素整体向后挪动一位,然后再在pos处插入数据。

//void SLInsert(SL* psl, int pos, SLDataType x)
void SLInsert(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos <= psl->size);

	SLCheckCapacity(psl);

	// 挪动数据
	/*int end = psl->size - 1;
	while (end >= (int)pos)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}*/

	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end-1];
		--end;
	}

	psl->a[pos] = x;
	++psl->size;
}

由于尾插和头插也可以通过调用 SeqListInsret 函数实现,所以我们可以对头插和尾插函数进行改造,以此来简化代码:

在头部插入数据

void SLPushFront(SL* psl, SLDataType x)
{
	SLInsert(psl, 0, x);
}

在尾部插入数据

void SLPushBack(SL* psl, SLDataType x)
{
	SLInsert(psl, psl->size, x);
}

7、在尾部删除数据

删除尾部的数据很简单,我们只需要将size–即可,并不需要对其进行改动,因为我们下一次插入数据时会直接将原来空间中的数据覆盖掉。

void SLPopBack(SL* psl)
{
	//assert(psl);

	 温柔的检查
	///*if (psl->size == 0)
	//{
	//return;
	//}*/

	 暴力的检查
	//assert(psl->size > 0);

	//psl->size--;
	SLErase(psl, psl->size - 1);
}

8、在头部删除数据

在头部删除数据,我们只需要将顺序表中的数据整体向前挪动一位,然后将size–即可。

void SLPopFront(SL* psl)
{
	//assert(psl);
	//assert(psl->size > 0);

	///*int begin = 0;
	//while (begin < psl->size-1)
	//{
	//	psl->a[begin] = psl->a[begin + 1];
	//	++begin;
	//}*/

	//int begin = 1;
	//while (begin < psl->size)
	//{
	//	psl->a[begin-1] = psl->a[begin];
	//	++begin;
	//}

	//--psl->size;
	SLErase(psl, 0);
}

9、删除指定位置的数据

删除指定位置数据,我们需要将pos后面的数据整体向前挪动一位,然后让size–。

void SLErase(SL* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);

	size_t begin = pos;
	while (begin < psl->size - 1)
	{
		psl->a[begin] = psl->a[begin + 1];
		++begin;
	}

	psl->size--;
}

和上面的插入数据一样,我们也可以通过调用 SeqListErase 函数来实现数据的头删和尾删。


**面试题:删除数据是否要缩容?**

我们知道,插入数据空间不够时我们要增容,那么删除数据达到一定的数量后我们是否要缩容呢?答案是不用缩容。原因如下:

第一:我们缩容之后插入数据又需要重新增容,而增容是有代价的,会降低程序的效率。我们知道 realloc 函数扩容分为两种情况,一种是原地扩,即当原来的空间后面有足够的空闲空间时,操作系统会直接将那一块空间交由我们使用,这种情况对效率影响不大;另一种是异地扩,即当原来空间后面没有足够的的空间开辟时,操作系统会在另外空间足够的地方为我们开辟一块新的空间,这时操作系统需要先将我们原来空间中的数据拷贝到新空间中,再将原来的空间释放掉,这种情况对效率的影响就比较大了。

第二:缩容也是有代价的。其实缩容和扩容的过程是一样的,都分为原地和异地,会对程序效率造成影响。

第三:顺序表申请的是一块连续的空间,而free函数数并不能释放连续空间的一部分,只能全部一起释放,所以这里即使想释放也是做不到的。

所以综合前面三个因素考虑,顺序表删除数据不会缩容;这是我们典型的**以空间换时间**的做法。

10、查找数据

当我们找到该元素时,我们返回元素的下标;当该元素不存在时,我们返回一个无意义的值。(如-1)

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

	for (int i = 0; i < psl->size; ++i)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

11、修改指定位置的数据

void SLModify(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size);

	psl->a[pos] = x;
}

12、打印顺序表中的数据

void SLPrint(const SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

13、顺序表的销毁

在销毁顺序表的时候我们一定要记得将前面动态开辟的空间释放掉,防止内存泄漏。

void SLDestory(SL* psl)
{
	assert(psl);

	/*if (psl->a)
	{*/
		free(psl->a);
		psl->a = NULL;
		psl->capacity = psl->size = 0;
	//}
}

四、完整代码

1、SeqLIst.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//#ifndef __SEQLIST_H__
//#define __SEQLIST_H__
...
//#endif

// Shunxubiao -- 二狗  铁柱  招弟

// 静态的顺序表 -- 不知道要存储多少数据?
//#define N 10000
//typedef int SLDataType;
//struct SeqList
//{
//	SLDataType a[N];
//	int size; // 存储数据的个数
//};

// 动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a;
	int size;      // 存储数据的个数
	int capacity;  // 存储空间的大小
}SL;

//void SeqListInit(SL sl);
void SLInit(SL* psl);
void SLDestory(SL* psl);
void SLPrint(const SL* psl);

// 头插头删 尾插尾删
void SLPushBack(SL* psl, SLDataType x);
void SLPushFront(SL* psl, SLDataType x);
void SLPopBack(SL* psl);
void SLPopFront(SL* psl);

// 没有找到就返回-1
int SLFind(SL* psl, SLDataType x);

// 顺序表在pos位置插入x
void SLInsert(SL* psl, size_t pos, SLDataType x);

// 顺序表删除pos位置的值
void SLErase(SL* psl, size_t pos);

void SLModify(SL* psl, size_t pos, SLDataType x);

2、SeqList.c

#include "SeqList.h"

void SLPrint(const SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

void SLInit(SL* psl)
{
	assert(psl);

	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

void SLDestory(SL* psl)
{
	assert(psl);

	/*if (psl->a)
	{*/
		free(psl->a);
		psl->a = NULL;
		psl->capacity = psl->size = 0;
	//}
}

void SLCheckCapacity(SL* psl)
{
	// 检查容量
	if (psl->size == psl->capacity)
	{
		int newCapcity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, newCapcity*sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
			//exit(-1);
		}

		psl->a = tmp;
		psl->capacity = newCapcity;
	}

}

// https://cplusplus.com/reference/
void SLPushBack(SL* psl, SLDataType x)
{
	/*assert(psl);

	SLCheckCapacity(psl);

	psl->a[psl->size] = x;
	psl->size++;*/

	SLInsert(psl, psl->size, x);
}

void SLPushFront(SL* psl, SLDataType x)
{
	//assert(psl);
	//SLCheckCapacity(psl);

	 挪动数据
	//int end = psl->size - 1;
	//while (end >= 0)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	--end;
	//}
	//psl->a[0] = x;
	//psl->size++;

	SLInsert(psl, 0, x);
}

void SLPopBack(SL* psl)
{
	//assert(psl);

	 温柔的检查
	///*if (psl->size == 0)
	//{
	//return;
	//}*/

	 暴力的检查
	//assert(psl->size > 0);

	//psl->size--;
	SLErase(psl, psl->size - 1);
}

void SLPopFront(SL* psl)
{
	//assert(psl);
	//assert(psl->size > 0);

	///*int begin = 0;
	//while (begin < psl->size-1)
	//{
	//	psl->a[begin] = psl->a[begin + 1];
	//	++begin;
	//}*/

	//int begin = 1;
	//while (begin < psl->size)
	//{
	//	psl->a[begin-1] = psl->a[begin];
	//	++begin;
	//}

	//--psl->size;
	SLErase(psl, 0);
}

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

	for (int i = 0; i < psl->size; ++i)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

//void SLInsert(SL* psl, int pos, SLDataType x)
void SLInsert(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos <= psl->size);

	SLCheckCapacity(psl);

	// 挪动数据
	/*int end = psl->size - 1;
	while (end >= (int)pos)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}*/

	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end-1];
		--end;
	}

	psl->a[pos] = x;
	++psl->size;
}

// 顺序表删除pos位置的值
void SLErase(SL* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);

	size_t begin = pos;
	while (begin < psl->size - 1)
	{
		psl->a[begin] = psl->a[begin + 1];
		++begin;
	}

	psl->size--;
}

void SLModify(SL* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size);

	psl->a[pos] = x;
}

3、test.c

//#include <stdio.h>
//#include <stdlib.h>
//
//void f1(int n)
//{
//	int a = 0;
//	int* p = (int*)malloc(4 * n);
//
//	printf("%p,%p\n", &a, &p);
//	if(p == NULL)
//	{
//		perror("malloc fail");
//		return;
//	}
//
//	free(p);
//}
//
 2N+2 个数
 N+1  个数
//int main()
//{
//	f1(10);
//	f1(10);
//
//	return 0;
//}
#include "SeqList.h"

void TestSeqList1()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPushBack(&s, 6);
	SLPrint(&s);

	SLPushFront(&s, 10);
	SLPushFront(&s, 20);
	SLPushFront(&s, 30);
	SLPrint(&s);

	SLDestory(&s);
}

void TestSeqList2()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);

	SLPopBack(&s);
	SLPopBack(&s);
	SLPrint(&s);

	SLPopBack(&s);
	SLPopBack(&s);
	SLPrint(&s);

	//SLPopBack(&s);
	//SLPrint(&s);

	SLPushBack(&s, 10);
	SLPushBack(&s, 20);
	SLPushBack(&s, 30);
	SLPushBack(&s, 40);
	SLPrint(&s);

	SLDestory(&s);
}

void TestSeqList3()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);

	SLPopFront(&s);
	SLPopFront(&s);
	SLPrint(&s);

	SLPopFront(&s);
	SLPopFront(&s);
	SLPrint(&s);

	SLPushBack(&s, 10);
	SLPushBack(&s, 20);
	SLPushBack(&s, 30);
	SLPushBack(&s, 40);
	SLPrint(&s);

	int* p1 = (int*)malloc(sizeof(int)* 10);
	assert(p1);
	printf("p1:%p\n", p1);

	int* p2 = (int*)realloc(p1, sizeof(int)* 5);
	assert(p2);
	printf("p2:%p\n", p2);
}

void TestSeqList4()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPrint(&s);
	
	//SLInsert(&s, 2, 30);
	SLInsert(&s, 0, 30);
	SLPrint(&s);

	int x = 0;
	scanf("%d", &x);
	int pos = SLFind(&s, x);
	if (pos != -1)
	{
		SLInsert(&s, pos, x * 100);
	}
	SLPrint(&s);
}

void TestSeqList5()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPrint(&s);

	SLErase(&s, 0);
	SLPrint(&s);

	int x = 0;
	scanf("%d", &x);
	int pos = SLFind(&s, x);
	if (pos != -1)
	{
		SLErase(&s, pos);
	}
	SLPrint(&s);
}

void memu()
{
	printf("****************************************\n");
	printf("1、尾插数据 2、头插数据\n");
	printf("7、打印数据 -1、退出\n");
	printf("****************************************\n");
}

int main()
{
	SL s;
	SLInit(&s);
	int option = 0;
	int x = 0;
	do
	{
		memu();
		printf("请输入你的操作:>");
		scanf("%d", &option);
		switch (option)
		{
			case 1:
				printf("请连续输入你要插入的数据,以-1结束\n");
				scanf("%d", &x);
				while (x != -1)
				{
					SLPushBack(&s, x);
					scanf("%d", &x);
				}
				break;
			case 2:
				break;
			case 3:
				break;
			case 7:
				SLPrint(&s);
				break;
			default:
				break;
		}

	} while (option != -1);

	SLDestory(&s);

	return 0;
}

五、顺序表的缺陷

顺序表存在如下缺陷:

  • 在头部/中间的插入与删除需要挪动数据,时间复杂度为O(N),效率低;
  • 增容需要申请新空间,可能会拷贝数据,释放旧空间,会有不小的消耗;
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,如果我们再继续插入了5个数据,后面没有数据插入了,那么会浪费95个数据空间;

针对顺序表存在的这些缺陷,我们设计出了链表。


六、顺序表力扣OJ题

1、移除元素

题目链接

27. 移除元素 - 力扣(LeetCode)

题目描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。image-20220728174857575

思路分析

思路1

循环遍历,挪动覆盖数据:遍历这个数组,每当遇见等于val的元素时,就将数组后面的元素整体向前挪动一位。
在这里插入图片描述

时间复杂度:O(N^2) 空间复杂度:O(1)。

思路2

遍历数组,挪动元素到新空间:开辟一个新的数组,然后遍历原来的数组,将不等于val的元素放入新开辟的数组中,将等于val的元素丢弃;最后让新开辟的数组覆盖掉原数组。
在这里插入图片描述

时间复杂度:O(N) 空间复杂度:O(N) 因为开辟了一个新空间。

思路3

遍历数组,挪动覆盖数据(双指针解法):定义两个指针src 和 dst,最开始都指向数组第一个元素,然后开始遍历数组;如果arr[src] != val,那么让arr[dst] = arr[src],然后 src++,dst++;如果 arr[src]==val,则 src++,dst不动;这样一直往后遍历,知道把数组所有元素都遍历完。
在这里插入图片描述

思路三代码实现

int removeElement(int* nums, int numsSize, int val) {
    int src=0,dst=0;
    while(src < numsSize)
    {
        if(nums[src]==val)
        {
            ++src;
        }
        else
        {
            nums[dst]=nums[src];
            ++dst;
            ++src;
        }
    }
    return dst;
}

2、删除有序数组中的重复项

题目链接

26. 删除有序数组中的重复项 - 力扣(LeetCode)

题目描述

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。image-20220728200421875

思路分析

这道题的解题思路和上面的移除元素十分相似,都是使用双指针;让 src 和 dst 都指向数组第一个元素,然后判断 arr[src] 和 arr[dst] 是否相等,如果相等,说明是重复元素,则让 src++,dst 不动;如果不相等,就让dst++,然后将 arr[src] 赋给 arr[dst],再让 src++;然后一直往后遍历数组,直到将数组遍历完。

时间复杂度:O(N) 空间复杂度:O(1)

代码实现

int removeDuplicates(int* nums, int numsSize) {
    int src = 0;
    int dst = 0;
    
    while (src < numsSize) {
        if (nums[src] != nums[dst]) {
            nums[++dst] = nums[src++]; // 注意:dst 是前置++
        } else {
            src++;
        }
    }
    
    return dst + 1; // 由于 dst 是前置++,所以返回值需要 +1 (数组下标从0开始)
}

3、合并两个有序数组

题目链接

88. 合并两个有序数组 - 力扣(LeetCode)

题目描述

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。image-20220728201642640

思路分析

思路:开辟新数组,把数据有序的放入数组中(双指针法):因为两个原数组都是有序的,所以我们可以用两个指针 src1 和 src2 分别指向两个数组,如果src1 指向的元素小于 src2,就把src1的数据放入新数组中然后通过比较大小的方式把数据依次放入新数组中,最后新数组的数据覆盖掉原数组的数据。

在这里插入图片描述

思路代码实现

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int end1 = m - 1; // 指向数组1最后一个有效数据
    int end2 = n - 1; // 指向数组2末尾
    int end = m + n - 1; // 指向数组1末尾
    
    while (end1 >= 0 && end2 >= 0) { // 当其中一个数组遍历完成后循环结束
        if (nums1[end1] < nums2[end2]) 
        { 
        // 从后往前,需要找大的
            nums1[end--] = nums2[end2--];
        } 
        else 
        {
            nums1[end--] = nums1[end1--];
        }
    }
    
    // 如果数组1中还有元素则不用管,因为它本来就是有序的
    if (end2 >= 0) { // 如果数组2还有元素
        while (end2 >= 0) {
            nums1[end--] = nums2[end2--];
        }
    }
}


希望给大家带来帮助!!
一起加油! 我要去西电!!!!!

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024 年 2 月公链行业研报

作者&#xff1a;stellafootprint.network 数据来源&#xff1a;Footprint Analytics 公链研究页面 二月份&#xff0c;加密货币市场展现出强劲的上涨势头&#xff0c;这主要得益于比特币和以太坊的价值大幅上涨超过 45%。这一乐观态势也影响到其他代币&#xff0c;前十大代币…

iOS 判断触摸位置是否在图片的透明区域

装扮功能系列&#xff1a; Swift 使用UIScrollerView 实现装扮功能&#xff08;基础&#xff09;Swift 使用UIScrollerView 实现装扮功能&#xff08;拓展&#xff09;iOS 判断触摸位置是否在图片的透明区域 背景 在装扮功能中&#xff0c;一般都是长按使道具进入编辑状态&…

安装MySQL8.0及以上版本操作步骤

关于mysql安装过程中命令mysqld --initialize --console出错的解答 C:\mysql-8.3.0-winx64\bin>mysqld --initialize --usermysql --console 2024-03-12T11:21:23.201387Z 0 [System] [MY-015017] [Server] MySQL Server Initialization - start. 2024-03-12T11:21:23.2068…

【C语言】字符串函数上

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《C语言》 &#x1f389;道阻且长&#xff0c;行则将至 前言 这篇博客是字符串函数上篇&#xff0c;主要是关于长度不受限制的字符串函数&#xff08;strlen,strcpy,strcat,strcm…

报错:Nginx 部署后刷新页面 404 问题

文章目录 问题分析解决 问题 在部署完项目后 刷新页面&#xff0c;页面进入了404 分析 加载单页应用后路由改变均由浏览器处理&#xff0c;而刷新时将会请求当前的链接&#xff0c;而Nginx无法找到对应的页面 关键代码try_files,剩下俩如果其他地方配置了则可以省略。 在这…

网络安全等级测评师考试培训可以参考哪些资料?

网络安全是国家安全的重要组成部分&#xff0c;也是企业安全的重中之重&#xff1b;而网络安全等级测评师则是守护这一安全领域的重要力量。所以专业的网络安全等级测评师是非常重要。作为专业的网络安全等保测评师&#xff0c;他们肩负着对信息系统进行安全评估、发现潜在风险…

三星泄露微软 Copilot 新功能:用自然语言操控各种功能

3 月 11 日消息&#xff0c;微软计划本月晚些时候发布新款 Surface 电脑和适用于 Windows 11 的 Copilot 新功能&#xff0c;但三星似乎等不及了&#xff0c;在其即将推出的 Galaxy Book4 系列产品宣传材料中泄露了一些即将到来的 Copilot 功能。 三星官网上发布的图片证实了此…

leetcode刷题(javaScript)——堆相关场景题总结

堆是什么&#xff1f;堆都能用树表示&#xff0c;并且一般树的实现都是利用链表。平时使用的最多的是二叉堆&#xff0c;它可以用完全二叉树表示&#xff0c;二叉堆易于存储&#xff0c;并且便于索引。在堆的实现时注意&#xff1a;因为是数组&#xff0c;所以父子节点的关系就…

【智能算法】蝠鲼觅食优化算法(MRFO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2017年&#xff0c;Zhao等人受到蝠鲼自然捕食行为启发&#xff0c;提出了蝠鲼觅食优化算法(Manta Ray Foraging Optimization&#xff0c;MRFO)。 2.算法原理 2.1算法思想 MRFO模拟了蝠鲼在海洋中…

54、WEB攻防——通用漏洞跨域CORS资源JSONP回调域名接管劫持

文章目录 同源策略CORSJSONP跨域回调子域名劫持 同源策略 同源策略包括三个条件&#xff1a;同域名、同域名、同端口。同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。 CORS CORS&#xff08;跨域资源共享&#xff09;已被所有浏览器支持&#xff0c;跨…

简单了解 vim 编辑器最基础的操作

简单了解 vim 编辑器最基础的操作 vim 这个是 Linux 上自带的一个文本编辑器&#xff0c;使用 vim 就可以更灵活的对文件进行编辑了&#xff08;虽然和记事本的定位差不多,实际上vim的使用要复杂很多&#xff09; 1.打开文件 语法&#xff1a;vim 文件名 示例&#xff1a;…

简单理解NAT模式和桥接模式

目录 桥接模式NAT模式总结 桥接模式 1.桥接模式下 当物理机X创建了一台或多台虚拟机 那么这些创建出来的虚拟机 可以视作一台独立的新机器 加入了该局域网 并允许和该局域网的物理机或者其他虚拟机直接通信 2.问题一在于 C类网的分配是有范围的(0-255) 假如是一个教室里的局域…

智慧公厕建设,助力打造宜居、韧性、可持续的智慧城市

公共厕所作为智慧城市的重要组成部分&#xff0c;对于城市的高质量发展起着至关重要的作用。智慧公厕建设旨在通过全面监测、控制和管理公共厕所&#xff0c;实现多方面功能&#xff0c;包括公共厕所环境监测与调控、厕位占用监测与引导、消耗品监测与缺失提示、安全防范与管理…

WPF监控平台(科技大屏)[一]

跟着B站的视频敲了一个略微复杂的WPF界面,链接如下.在这里我详细的写一份博客进行设计总结. 系统介绍和配置及主窗口设计_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Wy421Y7QD?p1&vd_source4796b18a2e4c1ec8a310391a5644b6da 成果展示 实现过程 总体来说,我的…

Shell常用脚本:hadoop集群启动、停止、重启脚本

脚本内容以我搭建的hadoop集群为例&#xff0c;你们自用的时候自行根据你们的情况进行修改即可 hadoop-cluster-manager.sh #!/bin/bash # 1. 调用此脚本前&#xff0c;请使用ssh-keygen -t rsa、ssh-copy-id -f 目标机器这两个命令使得目标机器是免密登录的 # 2. ssh远程执行…

Linux常用操作命令和服务器硬件基础知识

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

嵌入式学习day37 数据结构

1.sqlite3_open int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 功能: 打开数据库文件(创建一个数据库连接) 参数: filename:数据库文…

【智能硬件、大模型、LLM 智能音箱】MBO:基于树莓派、ChatGPT 的桌面机器人

MAKER:David Packman/译:趣无尽(转载请注明出处) 这是国外 Maker David Packman 制作的基于树莓派机器人 MBO,该机器人的外观设计灵感来自动漫 Adventure Time 中的机器人 MBO。它具有强大的交互功能,可实现脱机唤醒词检测、调用 ChatGPT 3.5 进行聊天、机器视觉对图像进…

【开源】SpringBoot框架开发班级考勤管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统基础支持模块2.2 班级学生教师支持模块2.3 考勤签到管理2.4 学生请假管理 三、系统设计3.1 功能设计3.1.1 系统基础支持模块3.1.2 班级学生教师档案模块3.1.3 考勤签到管理模块3.1.4 学生请假管理模块 3.2 数据库设…

实战 | 基于YOLOv9和OpenCV实现车辆跟踪计数(步骤 + 源码)

导 读 本文主要介绍使用YOLOv9和OpenCV实现车辆跟踪计数&#xff08;步骤 源码&#xff09;。 实现步骤 监控摄像头可以有效地用于各种场景下的车辆计数和交通流量统计。先进的计算机视觉技术&#xff08;例如对象检测和跟踪&#xff09;可应用于监控录像&#xff0c;…