数据结构初阶--顺序表

目录

一.顺序表的定义

二.顺序表的分类

2.1.静态顺序表

2.2.动态顺序表

三.顺序表的特点

四.顺序表的功能实现

4.1.顺序表的定义

4.2.顺序表的初始化

4.3.顺序表的销毁

4.4.顺序表的容量检查

4.5.顺序表的打印

4.6.顺序表的尾插

4.7.顺序表的头插

4.8.顺序表的尾删

4.9.顺序表的头删

4.10.顺序表的任意位置插入

4.11.顺序表的任意位置删除

4.12.顺序表的查找某个元素

4.13.顺序表的某个位置上的值的修改

4.14.完整程序

SeqList.h

SeqList.c

test.c

五.顺序表刷题

题一:移除元素

题二:去重算法

题三:合并两个有序数组


一.顺序表的定义

线性表:是具有相同数据类型的n(n>=0)个数据元素的有限序列。

顺序表:用顺序存储的方式实现线性表。

顺序存储:把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。

二.顺序表的分类

2.1.静态顺序表

#define MaxSize 100 //定义最大长度
typedef int ElemType;

typedef struct
{
	ElemType data[MaxSize];//用静态的数组存放数据元素
	int length;//顺序表的当前长度
}SeqList;//顺序表的类型定义

//初始化一个顺序表
void InitList(SeqList* L)
{
	L->length = 0;//顺序表初始长度为0
}

声明一个顺序表时在内存中分配存储顺序表L的连续空间。包括:MaxSize*sizeof(ElemType)和存储Length的空间。                                                                                                                                         初始化顺序表时可以不用把各个数据元素的值设为默认值,直接将Length的值设为0即可。尽管内存中会有遗留的“脏数据”,但是因为访问超过当前长度的值是违法的,所以并不会有什么影响。尽量使用基本操作来访问顺序表中的各个数据元素。

2.2.动态顺序表

因为静态顺序表的表长开始确定后就无法更改(存储空间是静态的),刚开始就声明一个很大的内存空间又很浪费。所以可以用动态分配的方式实现顺序表大小可变。

#define InitSize 100  //顺序表的初始长度
typedef int ElemType;

typedef struct
{
	ElemType* data;//指示动态分配数组的指针
	int MaxSize;//顺序表的最大容量
	int length;//顺序表的当前长度
}SeqList;//顺序表的类型定义

//初始化一个顺序表
void InitList(SeqList* L)
{
	L->length = 0;//顺序表初始长度为0
	L->MaxSize = InitSize;
}

C提供了malloc和free函数来实现动态申请和释放内存空间。使用时加头文件#include<stdlib.h>。malloc函数会申请一整片连续的存储空间,返回指向这片存储空间开始地址的指针。需要强制转型来决定这片存储空间用来存储什么类型的数据元素。如

L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);

就强制转型为了你定义的数据元素类型指针,并让data指针变量指向这一整片存储空间的起始地址,也就是顺序表的第一个数据元素。

三.顺序表的特点

四.顺序表的功能实现

4.1.顺序表的定义

typedef int SLDataType;

//静态顺序表:N太小,可能不够用;N太大,可能浪费空间
//动态顺序表:大小可以自动调节
typedef struct SeqList
{
	SLDataType* a;//动态数组
	int size;//当前元素个数
	int capacity;//空间大小
}SL;

4.2.顺序表的初始化

void SLInit(SL* ps)
{
	assert(ps);
	//assert(ps!=NULL);

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

需要注意的是:实参传给形参,是将实参的值拷贝给形参,对形参的值进行修改并不会影响实参,因为它是值传递,所以需要改成传地址。

调试分析:

4.3.顺序表的销毁

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

	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;

		ps->capacity = ps->size = 0;
	}
}

在进行动态内存分配时,需要调用malloc,realloc等函数进行内存空间的开辟。在使用完毕之后,要对所开辟的内存空间进行回收,这时就要调用free函数进行内存空间的释放。

调试分析:

4.4.顺序表的容量检查

void SLCheckCapacity(SL* ps)
{
	assert(ps);

	//检查容量空间,若容量为空,则将元素个数设置为4;若容量满了,则将空间容量设置为起始容量的二倍
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//若capacity的初始值为0,则扩容4个元素,否则,容量扩大两倍

		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));//原地扩容,异地扩容

		if (tmp == NULL)
		{
			perror("realloc");
			//return;
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}

ps->size < ps->capacity时,此时进行元素的插入显然是不需要对容量进行检查的。只有当ps->size == ps->capacity时,此时进行元素的插入才会对容量进行检查。                                      当ps->size == ps->capacity == 0时,若要插入元素,此时容量为空,无法插入新的元素,通过调用realloc函数开辟4个元素的内存空间。当ps->size == ps->capacity != 0时,若要插入元素,此时容量已满,无法插入新的元素,通过调用realloc函数开辟2*ps->capacity个元素的内存空间。

调试分析:

4.5.顺序表的打印

void SLPrint(SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; ++i)
	{
		printf("%d ",ps->a[i]);
	}

	printf("\n");
}

即将顺序表的元素依次输出。

4.6.顺序表的尾插

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

	//检查容量
	SLCheckCapacity(&ps);

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

在插入元素之前,首先要做的就是对空间容量的检查。若空间已满,则进行扩容再在末尾插入新的元素;若空间未满,则直接在末尾插入新的元素。                                                                                    先在数组下标为size的位置插入新的元素x,再将ps->size++。

调试分析:

运行结果:

4.7.顺序表的头插

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

	//检查容量
	SLCheckCapacity(ps);

	//从后往前移
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//放入数据
	ps->a[0] = x;
	ps->size++;
}

同尾插一样,在插入元素之前需要对空间容量进行判断。若空间充足,则要把第0个位置开始的ps->size个元素全部后移一位,再在第0个位置上填入要插入的元素x;若空间不足,则先扩容再在数组头部插入元素x。

调试分析:

运行结果:

4.8.顺序表的尾删

void SLPopBack(SL* ps)
{
	assert(ps);

	//温柔检查
	//if (ps->size == 0)
	//{
	//	printf("SeqList is empty\n");
	//	return;
	//}
	
	//暴力检查
	assert(ps->size > 0);

	ps->size--;
}

在对数组进行尾删时,可能会存在越界问题。当不使用assert(ps->size > 0)时,越界并不报错,只有在销毁SLDestory(&s1)之后,才会对越界问题进行检查与报错。需要注意的是:越界是不一定报错的,系统对越界的检查,是设岗抽查。

案例分析:

在尾插了5个元素之后,又进行了6次尾删,此时的ps->size=-1,显然已经越界,但是系统并没有报错。 所以针对潜在的越界问题,需要进行特殊处理。通常采用的方法是:在执行ps->size--时,先进行assert(ps->size > 0)判断。

在加上assert(ps->size > 0)判断之后,再执行程序得:

4.9.顺序表的头删

void SLPopFront(SL* ps)
{
	assert(ps);

	assert(ps->size > 0);

	//从前往后移动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	ps->size--;
}

头删与尾删相类似,都存在数组越界问题,在删除之前都需要进行assert(ps->size > 0)的判断,案例与尾删相同,这里就不再进行举例分析。

调试分析:

运行结果:

4.10.顺序表的任意位置插入

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps!=NULL);
	assert(pos >= 0 && pos <= ps->size);

	//检查容量
	SLCheckCapacity(ps);

	//从后往前移动数据
	int end = ps->size - 1;

	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}

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

在插入数据之前,要进行插入位置pos合法性的判断。首先pos >= 0,因为数组的下标是从0开始的。其次pos <= ps->size,因为数组的最大下标为ps->size-1,而插入的位置可以在最后一个元素的后面。插入的位置一定要保证数组的连贯性,中间不能存在空缺。因此,为了避免下标越界,在插入数据之前,需要进行assert(pos >= 0 && pos <= ps->size)的判断。

同时,在插入元素时,一定要判断空间容量的大小,若不足则直接调SLCheckCapacity(ps)进行扩容。

调试分析:

运行结果:

拓展:

既然函数void SLInsert(SL* ps, int pos, SLDataType x)是在任意位置进行插入,那么我们可以将顺序表的头插函数void SLPushFront(SL* ps, SLDataType x)与尾插函数void SLPushBack(SL* ps, SLDataType x)进行改写:

顺序表的头插:

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

顺序表的尾插:

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

4.11.顺序表的任意位置删除

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);//不能等于size

	/*
	//注意越界问题
	//法1:
	int begin = pos;
	while (begin < ps->size-1)
	{
		ps->a[begin] = ps->a[begin+1];
		++begin;
	}
	*/

	//法2
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	ps->size--;
}

在删除数据之前,要进行删除位置pos合法性的判断。首先pos >= 0,因为数组的下标是从0开始的。其次pos < ps->size,因为数组的最大下标为ps->size-1。因此,为了避免下标越界,在删除数据之前,需要进行assert(pos >= 0 && pos < ps->size)的判断。

调试分析:

运行结果:

4.12.顺序表的查找某个元素

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

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

	return -1;
}

案例:

void test()
{
	SL s1;
	SLInit(&s1);

	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLPushFront(&s1, 5);
	SLPrint(&s1);

	int x = 0;
	printf("请输入你要查找的值:");
	scanf("%d", &x);
	int pos = SLFind(&s1, x);
	//返回对应元素的下标
	printf("查找元素的下标为:%d\n",pos);

	//销毁
	SLDestory(&s1);
}

int main()
{
	test();

	return 0;
}

运行结果:

扩展:

如何查找顺序表中某个元素,并删除所有相同的元素?

与函数int SLFind(SL* ps, SLDataType x)不同的是,函数int SLFindPlus(SL* ps, SLDataType x, int begin)多了一个起始的begin值以及修改了这个循环的起始条件。

案例:

int SLFindPlus(SL* ps, SLDataType x, int begin)
{
	assert(ps);
	for (int i = begin; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void Test()
{
	SL s1;
	SLInit(&s1);

	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLPushFront(&s1, 3);
	SLPrint(&s1);

	int x = 0;
	printf("请输入你要删除的值:");
	scanf("%d", &x);
	//查找某个值
	int pos = SLFind(&s1, 3, 0);

	while (pos != -1)
	{
		SLErase(&s1, pos);
		pos = SLFind(&s1, 3, pos);//更新下一个位置
	}

	SLPrint(&s1);
}

int main()
{
	Test();

	return 0;
}

运行结果:

4.13.顺序表的某个位置上的值的修改

void SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

案例:

void test()
{
	SL s1;
	SLInit(&s1);

	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLPushFront(&s1, 5);
	SLPrint(&s1);

	int y, z = 0;
	printf("请输入你要修改的值和修改后的值:");
	scanf("%d %d", &y, &z);
	//查找某个位置
	int pos = SLFind(&s1, y);

	if (pos != -1)
	{
		SLModify(&s1, pos, z);
	}
	else
	{
		printf("没有找到:%d\n", y);
	}

	//打印
	SLPrint(&s1);

	//销毁
	SLDestory(&s1);
}

int main()
{
	test();

	return 0;
}

运行结果:

4.14.完整程序

SeqList.h

#pragma once

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

typedef int SLDataType;

//静态顺序表:N太小,可能不够用;N太大,可能浪费空间
//动态顺序表:大小可以自动调节
typedef struct SeqList
{
	SLDataType* a;//动态数组
	int size;//当前元素个数
	int capacity;//空间大小
}SL;

//尾插,尾删的时间复杂度为:O(1)
//头插,头删的时间复杂度为:O(N)

//初始化
void SLInit(SL* ps);

//检查容量
void SLCheckCapacity(SL* ps);

//打印
void SLPrint(SL* ps);

//销毁
void SLDestory(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType x);

//头插
void SLPushFront(SL* ps, SLDataType x);

//尾删
void SLPopBack(SL* ps);

//头删
void SLPopFront(SL* ps);

//在某个位置插入
void SLInsert(SL* ps, int pos, SLDataType x);

//在某个位置删除
void SLErase(SL* ps, int pos);

//查找某个元素
int SLFind(SL* ps, SLDataType x);

//修改某个位置上的值
void SLModify(SL* ps, int pos, SLDataType x);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"SeqList.h"


//初始化
void SLInit(SL* ps)
{
	assert(ps);
	//assert(ps!=NULL);

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


//检查容量
void SLCheckCapacity(SL* ps)
{
	assert(ps);

	//检查容量空间,若容量为空,则将元素个数设置为4;若容量满了,则将空间容量设置为起始容量的二倍
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//若capacity的初始值为0,则扩容4个元素,否则,容量扩大两倍

		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));//原地扩容,异地扩容

		if (tmp == NULL)
		{
			perror("realloc");
			//return;
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}


//打印
void SLPrint(SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; ++i)
	{
		printf("%d ",ps->a[i]);
	}

	printf("\n");
}


//销毁
void SLDestory(SL* ps)
{
	assert(ps);

	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;

		ps->capacity = ps->size = 0;
	}
}


//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	/*
	assert(ps);

	//检查容量
	SLCheckCapacity(&ps);

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

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


//头插
void SLPushFront(SL* ps, SLDataType x)
{
	/*
	assert(ps);

	//检查容量
	SLCheckCapacity(ps);

	//从后往前移
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//放入数据
	ps->a[0] = x;
	ps->size++;
	*/

	SLInsert(ps, 0, x);
}


//尾删
void SLPopBack(SL* ps)
{
	/*
	assert(ps);

	//温柔检查
	//if (ps->size == 0)
	//{
	//	printf("SeqList is empty\n");
	//	return;
	//}
	
	//暴力检查
	//assert(ps->size > 0);

	ps->size--;
	*/
	
	SLErase(ps, ps->size-1);
}


//头删
void SLPopFront(SL* ps)
{
	/*
	assert(ps);

	assert(ps->size > 0);

	//从前往后移动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	ps->size--;
	*/

	SLErase(ps, 0);
}


//在某个位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps!=NULL);
	assert(pos >= 0 && pos <= ps->size);

	//检查容量
	SLCheckCapacity(ps);

	//从后往前移动数据
	int end = ps->size - 1;

	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}

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


//在某个位置删除
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);//不能等于size

	/*
	//注意越界问题
	//法1:
	int begin = pos;
	while (begin < ps->size-1)
	{
		ps->a[begin] = ps->a[begin+1];
		++begin;
	}
	*/

	//法2
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	ps->size--;
}


//查找某个元素
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

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

	return -1;
}

//修改某个位置上的值
void SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SeqList.h"

void TestList7()
{
	SL s1;

	//初始化
	SLInit(&s1);//实参传给形参,是将实参的值拷贝给形参,形参值的修改并不会影响实参,它是值传递,需要改成传地址

	//尾插
	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLPushFront(&s1, 5);

	//打印
	SLPrint(&s1);

	//在某个位置删除
	SLErase(&s1, 3);
	SLErase(&s1, 2);

	//打印
	SLPrint(&s1);

	//头删
	SLPopFront(&s1);

	//尾删
	SLPopBack(&s1);

	//打印
	SLPrint(&s1);

	//销毁
	SLDestory(&s1);
}

int main()
{
	TestList7();

	return 0;
}

五.顺序表刷题

题一:移除元素

题目描述:

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

分析:

法一:

覆盖删除  时间复杂度:O(N^2),空间复杂度:O(1)

找到所有的val,每次挪动val后的数据覆盖删除val。重复执行,直到删除所有的val。

实现:

int find(int* nums, int numsSize, int val)
{
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		if (nums[i] == val)
			return i;
	}
	return -1;
}

int removeElement(int* nums, int numsSize, int val)
{
	int ret = 0;
	while ((ret = find(nums, numsSize, val)) != -1)
	{
		for (int i = ret; i < numsSize - 1; i++)
		{
			nums[i] = nums[i + 1];
		}
		numsSize--;
	}
	return numsSize;
}

法二:

保留不是val的值,移到新数组  时间复杂度:O(N),空间复杂度:O(N)

创建一个临时数组tmp,然后遍历nums数组,把不是val的数值全都放到tmp数组,最后把tmp数组的内容依次拷贝到nums数组,并返回tmp数组长度。

int removeElement(int* nums, int numsSize, int val)
{
	if (numsSize == 0)//特殊处理
		return 0;

	int i = 0;
	int tmp[numsSize] = { 0 };

	int count = 0;
	for (i = 0; i < numsSize; i++)
	{
		if (nums[i] != val)
		{
			tmp[count] = nums[i];
			count++;
		}
	}

	for (i = 0; i < numsSize; i++)
	{
		nums[i] = tmp[i];
	}

	return count;
}

法三:

保留不是val的值,覆盖前面的值  时间复杂度:O(N),空间复杂度:O(1)

创建两个变量src和dst,初始时均指向nums首部。首先判断nums[src]是否等于val,如果等于val,则src向后移动,dst保持则不动。重复上述操作,当nums[src]!=val,令nums[dst]=nums[src],然后src、dst均向后偏移,直至src遍历完整个nums数组。

实现:

int removeElement(int* nums, int numsSize, int val)
{
	int src = 0;
	int dst = 0;

	while (src < numsSize)
	{
		if (nums[src] != val)
		{
			nums[dst++] = nums[src++];
		}
		else
		{
			src++;
		}
	}

	return dst;
}

题二:去重算法

描述:

删除有序数组中的重复项
给你一个升序排序的数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度,元素的相对顺序应该保持一致
不要使用额外的空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成

法一:

保留不是val的值,覆盖前面的值  时间复杂度:O(N),空间复杂度:O(1)

创建两个变量src和dst,初始时src=1,dst=0。首先判断nums[src]是否等于nums[dst],如果相等,则src向后移动,dst保持则不动;若不相等,则dst向后移动,同时将nums[src]的值赋给nums[dst],赋值成功之后,再将src向后移动一位。重复上述操作,直至scr遍历完整个数组。

实现:

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

	return dst + 1;
}

法二:

覆盖删除  时间复杂度:O(N),空间复杂度:O(1)
遍历整个数组,比较相邻两个元素是否相等,如果相等就把后一个相等元素后面的所有元素都往前移动,同时numsSize–。

int removeDuplicates(int* nums, int numsSize)
{
	for (int i = 0; i < numsSize - 1; i++) 
	{
		//如果相邻的元素相等,那么就用后面的元素覆盖前面的元素
		if (nums[i] == nums[i + 1]) 
		{
			for (int j = i; j < numsSize - 1; j++) 
			{
				nums[j] = nums[j + 1];
			}
			numsSize--; //覆盖结束后,数组元素个数-1 

			i--; //上面的程序执行完毕之后,i会自动i++,而因为后面的元素会覆盖前面的元素,所以下标要更新
		}

	}
	return numsSize;
}

题三:合并两个有序数组

描述:

给你两个按非递减顺序排列的整数数组nums1和nums2,另有两个整数m和n,分别表示nums1和nums2的元素数目
请你合并nums2到nums1中,使合并后的数组同样按非递减顺序排列
注意:最终,合并后的数组不应由函数返回,而是存在存储在数组nums1中。为了应对这种情况,nums1的初始长度为m+n

分析:

法一:开辟一个额外的数组  时间复杂度:O(n+m),空间复杂度:O(n+m)
法二:从后向前比较,然后逐一拷贝  时间复杂度:O(n+m),空间复杂度:O(1)
从nums1和nums2的最后一位有效位开始比较,然后由后向前,哪个元素大就放到数组nums1的最后的位置,重复上述操作,直到其中一个数组比较完毕。在比较完后也要进行一次判断:如果nums1已经比较完毕,而nums2还没有,那么直接把nums2剩余的元素都拷贝到nums1即可。

实现:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
	int end1 = m - 1;
	int end2 = n - 1;
	int end = m + n - 1;

	while (end1 >= 0 && end2 >= 0)
	{
		if (nums1[end1] > nums2[end2])
		{
			nums1[end--] = nums1[end1--];
		}
		else
		{
			nums1[end--] = nums2[end2--];
		}
	}

	//end1结束则合并结束,若end2未结束,则将end2剩余的元素全部拷贝到end1中
	while (end2 >= 0)
	{
		nums1[end--] = nums2[end2--];
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值