线性表的介绍,C语言实现顺序表以及有关的OJ题

1.线性表       

                线性表(linear list n个具有相同特性的数据元素的有限序列。 线性表是一种在实际
中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
               线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是
连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储.

2.顺序表

2.1概念及结          

                顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采
用数组存 储。在数组上完成数据的增删查改。
           
        顺序表一般可以分为:
                1. 静态顺序表:使用定长数组存储元素
        

                通常我们并不使用定长的顺序表,因此只需了解下即可。

2. 动态顺序表:使用动态开辟的数组存储。

下面我们来具体介绍一下动态顺序表:

        首先是类型的声明: 

// 确定顺序表内部存放数据的类型;
typedef int SLDateType;
//定义顺序表类型;
typedef struct SeqList
{
	SLDateType* a;
	int k;
	int capacity;

}SeList;

        其中有动态开辟的数组,有效数据个数,以及数组容量。 

初始化顺序表:

                给动态开辟的数组空间   初始化开辟3个元素的空间。

                此时数组容量为3,有效元素个数为0;

void SeqListInit(SeList* ps)
{
	ps->a = (SLDateType*)malloc(3 * sizeof(SLDateType));
	if (ps->a == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	ps->capacity = 3;
	ps->k = 0;
}

销毁顺序表;

                释放动态开辟的数组空间,然后容量与有效元素个数都为0即可。

//销毁顺序表;
void SeqListDestroy(SeList* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->k = 0;
}

打印顺序表

                只需要遍历一遍数组打印即可;

//打印顺序表;
void SeqListPrint(SeList* ps)
{
	int i = 0;

	for (i = 0; i < (ps->k); i++)
	{
		printf("%d ",*(ps->a+i));
	}
}

顺序表的扩容

                若有效元素的个数与容量相等则需要用realloc扩容,每次扩容,容量加倍。

//扩容函数;
void increaseSeqList(SeList* ps)
{
	if ((ps->k) == (ps->capacity))
	{
		SLDateType* tmp = (SLDateType*)realloc(ps->a, 2 * (ps->capacity) * (sizeof(SLDateType)));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}

顺序表的尾增:

                首先要判断数组需不需要扩容;然后输入需要尾增的数据,直接尾增进去即可;

//尾增
void SeqListPopBack(SeList* ps)
{
	//检查是否需要扩容;
	increaseSeqList(ps);
	//输入尾增数据;
	int n = 0;
	scanf("%d", &n);
	//尾增;
	ps->a[ps->k] = n;
	ps->k++;

}

尾删

         在k大于0的情况下直接有效元素数量k--即可;

//尾删
void SeqListPushBack(SeList* ps)
{
	assert(ps);
	assert(ps->k >0);

	ps->k--;
}

头删

        依次进行挪动覆盖在K--即可;

//头删;
void SeqListPushFront(SeList* ps)
{
	assert(ps);
	assert(ps->k > 0);

	int i = 0;
	for (i = 0; i < ps->k-1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}

	ps->k--;
}

头增

        分为两种情况若k==0则不需要扩容,也不需要挪动数据,直接插入即可,然后K++即可;

        若k不等与0:首先需要检查数组容量,然后需要从后往前挪动数据,在头插入数据 ,k++即可;

//头增;
void SeqListPopFront(SeList* ps)
{
	//检查容量
	increaseSeqList(ps);

	int n = 0;

	//不需要数据的挪动;
	if (ps->k == 0)
	{
		scanf("%d", &n);
		ps->a[0] = n;
		ps->k++;
		return;
	}

	//需要挪动覆盖:
	int i = 0;
	for (i = ps->k-1; i >=0; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}

	scanf("%d", &n);
	ps->a[0] = n;
	ps->k++;
}

顺序表查找

                遍历一遍数组若可以找到目标数据则返回其下标,若不能则返回-1;

// 顺序表查找
int SeqListFind(SeList* ps, SLDateType x)
{
	assert(ps->k > 0);
	int i = 0;
	for (i = 0; i < ps->k; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

顺序表在pos位置处插入x; 

                首先检查容量; 之后和头插一样分为两种情况:若k==0则不需要挪动数据直接插入,然后k++即可;若k不等于0则需要挪动数据再插入,然后K++;

// 顺序表在pos位置插入x
void SeqListInsert(SeList* ps, int pos, SLDateType x)
{
	//检查容量
	increaseSeqList(ps);

	//pos一定是大于等于0的数据;
	assert(pos >= 0);

	//k==0不需要挪动数据;
	if (ps->k == 0)
	{
		ps->a[pos] = x;
		ps->k++;
		return;
	}

	//k不等于0,需要挪动数据;
	int i = 0;
	for (i=ps->k-1;i>=pos;i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	ps->a[pos] = x;
	ps->k++;
}

顺序表在pos位置处删除数据;

                直接用后面的数据挪动覆盖,然后k--即可; 

// 顺序表删除pos位置的值
void SeqListErase(SeList* ps, int pos)
{
	
	assert(ps->k > 0);
	int i = 0;
	for (i = pos; i < ps->k-1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->k--;
}

leetcode题目

1.移除元素;leetcode链接:27. 移除元素 - 力扣(LeetCode)

 

分析: 此题要分为两种情况,若val在【0,n-2】位置处则需要进行数据的挪动,若在最后一个位置则不需要挪动,直接n--;

int removeElement(int* nums, int numsSize, int val)
{
    int i=0;
    int n=numsSize;
    //若为空数组:
    if(n==0)
    {
        return 0;
    }
    //在第一个数据和倒数第二个数据之间找val
    for(i=0;i<n-1;i++)
    {
        //找到,则将这个元素后面的数据进行向前挪动,
        if(nums[i]==val)
        {
            int j=i;
            for(j;j<n-1;j++)
            {
                nums[j]=nums[j+1];
            }
            //数组数据个数n--;
            n--;
            //i--防止挪动后的数据也为val,需要退回去对此位置重检查;
            i--;
        }
    }
    //若最后一个数据是val则不需要挪动,直接n--;
    if(nums[n-1]==val)
    {
        n--;
    }
    return n;
}

2.删除排序数组中的重复项 ;leetcode链接:26. 删除有序数组中的重复项 - 力扣(LeetCode)

 

                分析:只需要对数据进行遍历从后一个位置开始找相同的元素,若到相同的元素,则用后面的数据进行挪动覆盖; 

int removeDuplicates(int* nums, int numsSize)
{
    int n=numsSize;
    int i=0;
    //对数组进行遍历;
    for(i=0;i<n;i++)
    {
        int j=0;
        //从后一个位置开始找是否有重复的
        for(j=i+1;j<n;j++)
        {
            if(nums[i]==nums[j])
            {
                //若有则将后面的数据向前挪动,去除这个数据;如果是最后一个元素,则不需要挪动
                for(j;j<n-1;j++)
                {
                    nums[j]=nums[j+1];
                }
                n--;
                //j--防止挪动后的数据又和该数据相同,需要重新对该位置检查;
                j--;
                //i--对i当前的位置也要退回去重新检查;
                i--;
            }
        }
    }
    return n;
}

3.合并两个有序数组;   leetcode链接:88. 合并两个有序数组 - 力扣(LeetCode) 

 

         分析:此题很多人会对两个数组从前往后分别进行比较然后放入,但是这样会导致放入

num1时需要对元素整体后移动,比较麻烦。可以从后往前对两个数组元素进行比较,找较大的数

据从尾部放入num1,这样避免了对元素的移动。

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //num1的最后元素的下标;
    int end1=m-1;
    //num2的最后元素的下标;
    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--];
        }
    }
    //比较完后若nums2还没有遍历完将nums2剩下的元素放入;
    while(end2>=0)
    {
        nums1[end--]=nums2[end2--];
    }

}

 

 

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

(int*) nightfall

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值