顺序表(数据结构)

目录

线性表

顺序表

1、顺序表创建

2、初始化

3、扩容

4、尾插

5、尾删

6、头插

7、头删

8、指定位置插入

9、指定位置删除

10、查询

11、打印

12、销毁

顺序表总代码

Leetcode编程题

1、移除元素

题目链接:

题目描述:

题目解析:

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

题目链接:

题目描述:

题目解析

3、合并两个有序数组

题目链接:

题目描述:

题目解析:


线性表

概念:线性表是n个具有相同特性的数据元素的有限序列!

使用性:线性表是实际当中广泛使用的数据结构!

常见的线性表:顺序表、链表、栈、队列、字符串 等……


顺序表

概念:

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

结构:

1、静态顺序表:

使用定长数组来存储元素。也就是给数组指定的大小,来存储数组,但是有一个弊端,空间不知道给多大,给小了不够,给大了浪费,不建议写这类结构的顺序表!

2、动态顺序表:

使用动态开辟的数组来存储。就是用动态申请空间,通过一个指针来访问该空间,此时有一个好处,我们想要多大的空间就开多大的空间,空间不够还可以扩容,用多少开多少,比较静态顺序表,解决了空间不够,以及空间浪费的问题。比较推荐这类结构写顺序表!


本章将选择动态顺序表的结构形式往下写


1、顺序表创建

静态顺序表:指定数组大小

动态顺序表:动态开辟空间


定义:

我们首先定义一个结构体,让结构体里面存放一个数组(动态顺序表里存的是指向数组的指针),再存放一个整型变量,用来记录在数组种存放有效数据的个数!再通过typedef关键字,将这个结构体进行重命名操作!


代码:

//创建静态顺序表结构(不推荐这样写)

//用typedef重命名一下类型,
//后续我们需要放什么类型只需要改这一处即可
typedef int SLDateType;
#define N 100
typedef struct SeqList
{
	SLDateType a[N];//存放数据数组
	int size;//有效数据的个数
}SL;


//创建动态顺序表结构(推荐)

//用typedef重命名一下类型,
//后续我们需要放什么类型只需要改这一处即可
typedef int SLDateType;

//创建结构体,用动态增容的方式来控制空间
typedef struct SeqList
{
	SLDateType* a;//指向空间的指针
	int size;//有效数据的个数
	int capacity;//容量
}SL;

SL sl;//定义全局结构体变量以便我们进行操作

2、初始化

思路:

对于动态的顺序表结构,在初始化的时候需要动态申请一部分空间,让结构中的指针指向申请的空间。在申请的时候我们采用calloc函数来申请空间,因为calloc函数在动态开辟空间的时候会将空间中每个字节的内容都自动初始化为0,也就不需要我们自己再进行空间初始化了。开辟空间完成之后,将记录有效数据元素个数的整型变量初始化为0,再将记录空间个数的变量初始化成我们所开辟的空间个数即可!

在传函数参数的时候我们传的是结构体变量的地址,便于操作!


代码:

//结构体指针可通过->操作符,访问到结构体成员
#define IN 4

//初始化
void SLInit(SL* ps)
{
	//进行动态开辟空间,让指针指向空间
	ps->a = (SLDateType*)calloc(IN, sizeof(SLDateType));

	//判断空间是否开辟成功
	if (ps->a == NULL)
	{
		perror("calloc fail:");//提示失败错误
		return;
	}

	//初始化有效数据个数
	ps->size = 0;

	//初始化容量
	ps->capacity = IN;
}

3、扩容

思路:

当我们在往数组里装数据的时候,当我们一开始开辟的空间被装满了的情况下,我们就得对该空间进行扩容操作,扩容的时候用ralloc函数来进行扩容,每次增大多少容量呢,这是由自己掌控的,我们每次在原空间的基础上两倍两倍的来进行扩容操作。扩容完成之后让指针指向扩容后的空间,也让记录空间容量增加相同的倍数!


代码:

#define IT 2

//判断是否扩容
void SLCheckCapacity(SL* ps)
{
	//断言指针
	assert(ps);

	//当有效数据个数等于容量的时候进行扩容
	if (ps->size == ps->capacity)
	{
		//扩容
		SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity * IT);

		//判断是否开辟成功
		if (NULL == tmp)
		{
			perror("ralloc fail:");//提示失败信息
			return;
		}

		//扩容成功,让指针指向该空间
		ps->a = tmp;
		
		//让记录容量的变量增加
		ps->capacity *= IT;
	}
}

4、尾插

思路:

尾插,顾名思义就是在最后插入数据。在顺序表中尾插,就是在数组的最后一个元素后面插入一个数据,我们知道结构体里面有一个记录有效数据元素个数的变量,也就是数组元素的个数,数组元素的个数恰好是数组最后一个元素的下一个位置的下标。所以尾插的时候我们直接用记录元素个数的变量做为下标放入数据即可。放入之后,让记录元素个数的变量+1即可!但是在插入的时候我们还得关心我们开辟的空间是否够用?够用的情况下我们继续插入。不够用的情况下,我们就要进行扩容了。所以在插入的时候我们得先判断一下是否需要扩容。需要扩容,我们要先扩容再插入数据,不需要就直接插入数据!


代码:

//尾插
void SLPushBank(SL* ps, SLDateType x)
{
    //x是要插入的数据

	assert(ps);//断言一下指针是否为NULL

	//是否要扩容
	SLCheckCapacity(ps);

	//进行尾插
	ps->a[ps->size] = x;

	//记录元素的个数增加1
	ps->size++;
}

5、尾删

思路:

尾删,顾名思义就是将数组最后一个元素删除。要删除最后一个元素,直接将记录有效数据元素个数的变量-1即可,顺便将那个位置的数据改为0!但在删除的时候我们得注意一下数组中的数据是否删完了,如果删完了我们是不能进行删除的。会导致数组越界问题!


注意:

因为我们无法正真的去将最后一个元素所占的空间去释放,动态内存管理中是不允许释放连续空间中的某一部分空间的!用realloc函数去改变空间大小也是不行的,realloc函数是不支持空间缩容的,它要缩,只是缩了我们对空间的使用权!并没有将空间释放!所以我们只能通过这种减小有效数据个数来实现删除操作!


代码:

//尾删
void SLPopBank(SL* ps)
{
	//断言指针
	assert(ps);

	//判断数据是否删完了
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}
	
	//将要删除位置的数据改为0
	ps->a[ps->size - 1] = 0;

	//删除数据,让有效数据个数减少即可
	ps->size--;
}

6、头插

思路:

头插,顾名思义就是在数组的首位置插入一个元素,要在首位置插入元素,就要将数组中的每一个元素都往后移动一位,把首位置空出来,插入数据!插入数据的时候需要判断空间够不够,不够需要进行增容操作。随后让记录有效数据个数的变量+1即可!


代码:

//头插
void SLPushFont(SL* ps, SLDateType x)
{
	//x是要插入的数据
	
	//断言指针
	assert(ps);

	//是否要增容
	SLCheckCapacity(ps);

	//开始移动数据,让每个数据都往后移动一位
	int end = ps->size;
	while (end > 0)
	{
		ps->a[end] = ps->a[end - 1];
		--end;
	}

	//头插数据
	ps->a[0] = x;

	//记录个数
	ps->size++;
}

7、头删

思路:

头删,顾名思义就是将数组的首位置的数据删除,删除数据就是将数组从第二个元素开始之后的所有数据都往前挪动一位,将第一位的数据覆盖掉,实现头删操作。同时让记录元素个数的变量-1即可。在删除的时候先要进行判断数组是否有元素可删,也就是数组是否存在数据,若无数据是不能再删的!


代码:

//头删
void SLPopFont(SL* ps)
{
	//断言指针
	assert(ps);

	//判断数组是否有元素
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}

	//从第二个位置开始数组整体往前挪动一步覆盖掉第一个数据实现删除
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}

	//让记录个数的变量-1
	ps->size--;
}

8、指定位置插入

思想:

指定位置插入,就是指定数组的下标,在该位置插入一个数据。让数组从该位置开始每一个元素的数据,都往后挪动一步。随后在这个插入要插入的数据,且让记录数据个数的变量+1即可!在插入之前也需要判读是否要进行扩容操作!


代码:

//指定pos位置插入
void SLInsert(SL* ps, int pos, SLDateType x)
{
	//x是插入的数据
	//pos是要插入的位置
	
	//是否要增容
	SLCheckCapacity(ps);

	//断言指针
	assert(ps);

	//pos位置只能是0 - ps->size 之间,不然会越界
	assert(pos >= 0 && pos <= ps->size);
	//pos位置和ps->size相同时实际上是尾插
	//pos位置和0相同时实际上是头插

	//让pos位置之后的数据整体往后挪动一位
	// 再将x插入pos位置
	//开始挪动pos位置后面的数据
	int end = ps->size - 1;
	while (end > pos)
	{
		ps->a[end] = ps->a[end - 1];
		--end;
	}

	//插入数据
	ps->a[pos] = x;

	//记录个数变量+1
	ps->size++;
}

9、指定位置删除

思路:

指定位置删除,指定一个下标,删除数组对应的此下标位置的数据,从此下标位置的下一个位置开始之后数组的每一个数据都往前挪动一位,将该位置覆盖,进行删除操作,随后让记录个数的变量-1,且删除之前还要判断数组是否有元素,若没有元素不可再删除!


代码:

//指定pos位置删除
void SLErase(SL* ps, int pos)
{
	//pos是指定删除的位置
	
	//断言指针
	assert(ps);
	//pos位置只能是0 - ps->size 之间,不然会越界
	assert(pos >= 0 && pos <= ps->size);

	//判断数据是否删完了
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}

	//挪动数据覆盖pos位置删除
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	//记录个数变量-1
	ps->size--;
}

10、查询

思路:

查询数据,传入一个参数,这个参数是我们要查询的数据,随后遍历数组,找到与其相同的数据返回下标,若找不到则返回-1!


代码:

//查找
int SLFind(SL* ps, SLDateType x)
{
	//断言指针
	assert(ps);

	//遍历查找
	for (int i = 0; i < ps; i++)
	{
		if (ps->a[i] == x)
		{
			//返回下标
			return i;
		}
	}

	//找不到返回-1
	return -1;
}

11、打印

思路:

打印就是将数组从头到尾打印一边,将数组中的有效数据,进行遍历打印即可


代码:

//打印
void PrintSeqList(SL* ps)
{
	//断言指针
	assert(ps);

	//遍历打印
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

12、销毁

思路:

销毁就是,释放动态开辟的空间,随后将记录个数的变量置为0,记录容量的变量也置为0,最后将指向该空间的指针置为NULL,避免野指针!


注意:

这里的销毁不是真正的销毁,是将空间还给操作系统,释放空间,不是物理上的把空间销毁,空间还存在只是将空间还给了操作系统,不再使用到该空间!


代码:

//销毁
void SLDestory(SL* ps)
{
	//断言指针
	assert(ps);

	//释放开辟的空间
	free(ps->a);

	//有效个数置为0
	ps->size = 0;

	//容量置为0
	ps->capacity = 0;

	//将指针置为NULL,避免野指针
	ps->a = NULL;
}

顺序表总代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


//创建动态顺序表结构(推荐)

//用typedef重命名一下类型,
//后续我们需要放什么类型只需要改这一处即可
typedef int SLDateType;

//创建结构体,用动态增容的方式来控制空间
typedef struct SeqList
{
	SLDateType* a;//指向空间的指针
	int size;//有效数据的个数
	int capacity;//容量
}SL;

SL sl;//定义结构体变量以便我们进行操作

#define IN 4
#define IT 2

//初始化
void SLInit(SL* ps)
{
	//进行动态开辟空间,让指针指向空间
	ps->a = (SLDateType*)calloc(IN, sizeof(SLDateType));

	//判断空间是否开辟成功
	if (ps->a == NULL)
	{
		perror("calloc fail:");//提示失败错误
		return;
	}

	//初始化有效数据个数
	ps->size = 0;

	//初始化容量
	ps->capacity = IN;
}


//判断是否扩容
void SLCheckCapacity(SL* ps)
{
	//断言指针
	assert(ps);

	//当有效数据个数等于容量的时候进行扩容
	if (ps->size == ps->capacity)
	{
		//扩容
		SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity * IT);

		//判断是否开辟成功
		if (NULL == tmp)
		{
			perror("ralloc fail:");//提示失败信息
			return;
		}

		//扩容成功,让指针指向该空间
		ps->a = tmp;

		//让记录容量的变量增加
		ps->capacity *= IT;
	}
}


//尾插
void SLPushBank(SL* ps, SLDateType x)
{
	assert(ps);//断言一下指针是否为NULL

	//是否要扩容
	SLCheckCapacity(ps);

	//进行尾插
	ps->a[ps->size] = x;

	//记录元素的个数增加1
	ps->size++;
}

//尾删
void SLPopBank(SL* ps)
{
	//断言指针
	assert(ps);

	//判断数据是否删完了
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}

	//将要删除位置的数据改为0
	ps->a[ps->size - 1] = 0;

	//删除数据,让有效数据个数减少即可
	ps->size--;
}

//头插
void SLPushFont(SL* ps, SLDateType x)
{
	//x是要插入的数据

	//断言指针
	assert(ps);

	//是否要增容
	SLCheckCapacity(ps);

	//开始移动数据,让每个数据都往后移动一位
	int end = ps->size;
	while (end > 0)
	{
		ps->a[end] = ps->a[end - 1];
		--end;
	}

	//头插数据
	ps->a[0] = x;

	//记录个数
	ps->size++;
}

//头删
void SLPopFont(SL* ps)
{
	//断言指针
	assert(ps);

	//判断数组是否有元素
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}

	//从第二个位置开始数组整体往前挪动一步覆盖掉第一个数据实现删除
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}

	//让记录个数的变量-1
	ps->size--;
}

//指定pos位置插入
void SLInsert(SL* ps, int pos, SLDateType x)
{
	//x是插入的数据
	//pos是要插入的位置

	//是否要增容
	SLCheckCapacity(ps);

	//断言指针
	assert(ps);

	//pos位置只能是0 - ps->size 之间,不然会越界
	assert(pos >= 0 && pos <= ps->size);
	//pos位置和ps->size相同时实际上是尾插
	//pos位置和0相同时实际上是头插

	//让pos位置之后的数据整体往后挪动一位
	// 再将x插入pos位置
	//开始挪动pos位置后面的数据
	int end = ps->size - 1;
	while (end > pos)
	{
		ps->a[end] = ps->a[end - 1];
		--end;
	}

	//插入数据
	ps->a[pos] = x;

	//记录个数变量+1
	ps->size++;
}

//指定pos位置删除
void SLErase(SL* ps, int pos)
{
	//pos是指定删除的位置

	//断言指针
	assert(ps);
	//pos位置只能是0 - ps->size 之间,不然会越界
	assert(pos >= 0 && pos <= ps->size);

	//判断数据是否删完了
	if (ps->size == 0)
	{
		printf("没有数据可删\n");
		return;
	}

	//挪动数据覆盖pos位置删除
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}

	//记录个数变量-1
	ps->size--;
}

//查找
int SLFind(SL* ps, SLDateType x)
{
	//断言指针
	assert(ps);

	//遍历查找
	for (int i = 0; i < ps; i++)
	{
		if (ps->a[i] == x)
		{
			//返回下标
			return i;
		}
	}

	//找不到返回-1
	return -1;
}

//打印
void PrintSeqList(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);

	//释放开辟的空间
	free(ps->a);

	//有效个数置为0
	ps->size = 0;

	//容量置为0
	ps->capacity = 0;

	//将指针置为NULL,避免野指针
	ps->a = NULL;
}


int main()
{
	//以下部分可自行操作

	//初始化
	SLInit(&sl);

	//尾插
	SLPushBank(&sl, 1);
	SLPushBank(&sl, 2);
	SLPushBank(&sl, 3);
	SLPushBank(&sl, 4);
	SLPushBank(&sl, 5);
	PrintSeqList(&sl);//打印

	//尾删
	SLPopBank(&sl);
	SLPopBank(&sl);
	PrintSeqList(&sl);//打印

	//头插
	SLPushFont(&sl, 3);
	SLPushFont(&sl, 2);
	SLPushFont(&sl, 1);
	PrintSeqList(&sl);//打印

	//头删
	SLPopFont(&sl);
	SLPopFont(&sl);
	SLPopFont(&sl);
	PrintSeqList(&sl);//打印

	//指定pos位置插入
	SLInsert(&sl, 1, 5);
	PrintSeqList(&sl);//打印
	SLInsert(&sl, 0, 4);
	PrintSeqList(&sl);//打印
	SLInsert(&sl, 5, 7);
	PrintSeqList(&sl);//打印

	//指定pos位置删除
	SLErase(&sl, 0);
	PrintSeqList(&sl);//打印

	//查找
	int k = SLFind(&sl, 5);
	printf("%d", k);

	//销毁
	SLDestory(&sl);

	return 0;
}

Leetcode编程题

1、移除元素

题目链接:

27.移除元素https://leetcode.cn/problems/remove-element/


题目描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

题目解析:

思路1:

思路:

对数组进行遍历,找跟val相同的值,找到相同的值后,让数组的该位置(也就是跟val相同数据的位置)之后的数据整体进行往前挪动一位,覆盖掉该元素,同时让数组个数-1,实现删除操作!


画图理解:


代码: 

int removeElement(int* nums, int numsSize, int val)
{
    //遍历数组,找与val相同的值
    int i = 0;
    for (i = 0; i < numsSize; i++)
    {
        //判断是否与val相同,相同则进行删除
        if (val == nums[i])
        {
            //开始覆盖删除
            int j = 0;
            for (j = i; j < numsSize - 1; j++)
            {
                nums[i] = nums[i + 1];
            }
            //让数组个数进行-1
            numsSize--;

            //覆盖之后的i是与val相同值的下一个数据的下标
            //如果继续,执行,会产生i这个位置覆盖过来的新数据不会被判断到的请款
            //此时让i-1,再继续进行循环执行i++操作就能访问到覆盖过来的新数据
            i--;
        }
    }

    return numsSize;
}

思路2:

思路:

双指针法,直接在数组上进行操作,给定两个整型变量src和det,作为下标,初始值都给0,让src找与val相同的值,如果src位置的值与val相同,则让src++,不同则将src位置的值赋值给det位置,同时src++,det++。最后det就是数组的元素个数!


画图理解:


代码:

int removeElement(int* nums, int numsSize, int val)
{
    //让两个下标变量指向第一个元素
    int det = 0;
    int src = 0;
    
    //让src开始找,直到找完数组借书
    while (src < numsSize)
    {
        //相等让src++
        if (nums[src] == val)
        {
            ++src;
        }
        else
        {
            //不等则赋值过去,同时src++,det++
            nums[det++] = nums[src++];
        }
    }

    //此时det就是删除后数组元素个数
    return det;
}


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

题目链接:

26.删除有序数组中的重复项https://leetcode.cn/problems/remove-duplicates-from-sorted-array/


题目描述:

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

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

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 升序 排列

题目解析

思路:

首先,给定两个整型下标变量,src和det,让det表示首位置,src表示数组的第二个位置,

让src开始找与det位置不同的值,找到之后让det先往前走一步也就是++det,随后将src位置的值赋给det位置,同时src往后继续走一步src++,若src位置的值和det位置的值相同,则让src往后走一步src++,继续找,直到src找完数组为止。最后det+1就是去除重复数据后数组元素的个数!


画图理解:


代码: 

int removeDuplicates(int* nums, int numsSize) {

    //让det表示首元素,src表示第二个元素
    int det = 0;
    int src = 1;

    //让src开始找与det不同的值,相同让src继续走
   //不同则让det先走一步,再将src位置的值赋给det位置
    //直到找完数组位置
    while (src < numsSize)
    {
        if (nums[src] != nums[det])
        {
            det++;
            nums[det] = nums[src];
            src++;
        }
        else
        {
            src++;
        }
    }

    //最后det+1就是去重后数组的元素个数
    return det + 1;
}

3、合并两个有序数组

题目链接:

88.合并两个有序数组https://leetcode.cn/problems/merge-sorted-array/


题目描述:

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

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

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

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

题目解析:

思路1:

思路:

首先得理解一下非递减这个概念,非递减就是递增或相等。空间换时间的算法,我们首先开辟一个数组,让两个数组从头开始一一比较,把小的那一个值先放进开辟好的数组中,随后再放大的,相等的情况下放那个都可以。两个数组里面的数据比完之后,还要注意一个情况,两个数组中的某一数组的数据的有可能没有放完,因为不知道是那一个数组没放完,所以此时我们要对两个数组都进行判断,将没放完的数据放入开辟的数组空间中即可!最后将新数组的内容拷贝到nums1中!


画图理解:


代码: 

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //开辟新数组
    int* arr = (int*)malloc(sizeof(int) * (n + m + 1));
    int k = 0;
    //定义两个变量分别遍历两个数组
    int i = 0;
    int j = 0;
    while (i < m && j < n)
    {
        //开始比较大小
        if (nums1[i] < nums2[j])
        {
            //小的放进去
            arr[k++] = nums1[i++];
        }
        else
        {
            //相等的时候放nums2的值
            arr[k++] = nums2[j++];
        }
    }


    //看一下那个数组没放完,将剩余的放进去
    while (j < n)
    {
        arr[k++] = nums2[j++];
    }

    while (i < m)
    {
        arr[k++] = nums1[i++];
    }

    //进行拷贝
    memcpy(nums1,arr, sizeof(int) * k);

    //释放新数组
    free(arr);
}

思路2:

思路:

非递减就是递增或相等,我们定义三个下标变量i,j,det,分别表示:i是数组1的有效数据的最后一个数据的下标、j是数组2最后一个数据元素的下标、det是数组1最后一块空间的下标。随后让数组1的有效数据跟数组2的数据从后往前比较,将两个数组中较大的值拿到det的位置,同时让det和拿下数据的数组下标往前走一步,依次比较:最后会产生两种情况:情况1:数组2中的元素都放完了,此时数组1就是我们所要的归并后的最终数组。情况2:数组2中的元素没有放完,此时我们要将数组2中的剩余的数据都拿过来,拿过来之后数组1就是我们所要的最终数组!


画图理解:

 


代码:

//三个下标法
//  三下标写法
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
       //i 是nums1数组有效数据的最后一个元素的下标
    int i = m - 1;

    //j是nums2数组的最后一个元素的下标
    int j = n - 1;

    //det是nums1数组空间中的最后一个空间的下标
    int det = (m + n) - 1;

    //两个数组从前往后走对比大小
    while (i >= 0 && j >= 0)
    {
        //大的放到det位置同时进行--
        if (nums2[j] > nums1[i])
        {
            nums1[det--] = nums2[j--];
        }
        else
        {
            //相等的时候放nums2的值
            nums1[det--] = nums1[i--];
        }
    }

    //看nums2数组中的元素是否放完
    while (j >= 0)
    {
        nums1[det--] = nums2[j--];
    }
}

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值