一、顺序表
什么是顺序表:
顺序表表是用一段物理地址连续的存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改
要求数据是连续存入的
可动态增长的数据
属于线性表的一种
顺序表的定义:
顺序表的定义分为静态、动态两种,一般采用动态开辟,原因:
静态定义顺序表使用定长数组存储元素,开辟空间小了不够用,大了浪费空间。
静态顺序表:
//静态顺序表
#define N 10
typedef int SLDateType;
typedef struct seqlist
{
SLDateType a[N];
int size; //记录存储多少个有效数据
}SL;
动态顺序表:
typedef int DataType;
typedef struct SeqList
{
DataType* a;
int size;
int capacity;
}SeqList;
动态顺序表使用动态开辟空间存储数据元素
其中size为当前顺序表中的数据元素个数
capacity为顺序表能够村存数据的容量
顺序表的初始化:
void SeqListInit(SeqList* ps)
{
assert(ps);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
首先断言,防止传入空指针
释放动态顺序表:
void SeqListDestroy(SeqList* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
}
打印顺序表:
void SeqListPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
检查顺序表容量并扩容:
void CheckCapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
DataType* ptr = (DataType*)realloc(ps->a, sizeof(DataType) * newcapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = ptr;
ps->capacity = newcapacity;
}
}
注意判断realloc是否成功开辟空间
尾插:
void SeqListPushBack(SeqList* ps, DataType x)
{
//assert(ps);
//CheckCapacity(ps);
//ps->a[ps->size] = x;
//ps->size++;
SeqListInsert(ps, ps->size, x);
}
插入数据时需检查容量并判断是否扩容
尾删:
void SeqListPopBack(SeqList* ps)
{
//assert(ps);
//assert(ps->size > 0);
//ps->size--;
SeqListErase(ps, ps->size-1);
}
删除数据时需判断size的大小,防止size--到小于0,导致程序越界
测试尾插尾删功能:
void SeqListTest_Back()
{
SeqList SL;
SeqListInit(&SL);
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 4);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
SeqListPopBack(&SL);
SeqListPrint(&SL);
}
int main()
{
printf("尾插尾删测试:\n");
SeqListTest_Back();
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/78bce10482769c5140dc38ee88d5b04c.png)
头插:
void SeqListPushFront(SeqList* ps, DataType x)
{
//assert(ps);
//CheckCapacity(ps);
挪动数据
//int end = ps->size-1;
//while (end >= 0)
//{
// ps->a[end + 1] = ps->a[end];
// end--;
//}
//ps->a[0] = x;
//ps -> size++;
SeqListInsert(ps, 0, x);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a5d06ec65758aac98ab520ed175c8cf.png)
头删:
void SeqListPopFront(SeqList* ps)
{
//assert(ps);
//assert(ps->size > 0);
挪动数据
//int begin = 1;
//while (begin < ps->size)
//{
// ps->a[begin - 1] = ps->a[begin];
// begin++;
//}
//ps->size--;
SeqListErase(ps, 0);
}
![](https://i-blog.csdnimg.cn/blog_migrate/27ad7cd4c856d53d43894a0bdba6b696.png)
测试头插头删功能:
void SeqListTest_Front()
{
SeqList SL;
SeqListInit(&SL);
SeqListPushFront(&SL, 5);
SeqListPushFront(&SL, 4);
SeqListPushFront(&SL, 3);
SeqListPushFront(&SL, 2);
SeqListPushFront(&SL, 1);
SeqListPushFront(&SL, 0);
SeqListPrint(&SL);
SeqListPopFront(&SL);
SeqListPrint(&SL);
}
int main()
{
printf("头插头删测试:\n");
SeqListTest_Front();
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6e54b0f419afadcf70905a7366fe7503.png)
指定位置插入数据:
void SeqListInsert(SeqList* ps, int pos, DataType x)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
CheckCapacity(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前的数据
删除指定位置的数据:
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
由此我们可以推断出,当pos=0时,就是在顺序表中0的位置插入数据,即头插,以此类推,当pos = size时,就是在顺序表中size的位置插入数据,即尾插,删除的功能也是如此。
测试指定位置插入删除功能:
void SeqListTest_Insert_Earse()
{
SeqList SL;
SeqListInit(&SL);
//尾插填入数据
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
//测试
SeqListInsert(&SL, 3, 4);
SeqListInsert(&SL, 5, 6);
SeqListPrint(&SL);
SeqListErase(&SL, 5);
SeqListPrint(&SL);
}
int main()
{
printf("指定位置插入删除测试:\n");
SeqListTest_Insert_Earse();
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/d7edc08156076b2c9f58797f350ac20f.png)
在顺序表中查找某个数据:
int SeqListFind(SeqList* ps, DataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
测试查找功能:
void SeqListTest_Find()
{
SeqList SL;
SeqListInit(&SL);
//尾插填入数据
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 4);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
//查找顺序表中的5并删除
int ret = SeqListFind(&SL, 5);
if (ret != -1)
{
SeqListErase(&SL, ret);
}
SeqListPrint(&SL);
}
int main()
{
printf("查找数据测试:\n");
SeqListTest_Find();
return 0;
}
修改指定位置的数据:
void SeqListModify(SeqList* ps, int pos, DataType x)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
ps->a[pos] = x;
}
int main()
{
printf("测试修改指定位置数据功能:\n");
SeqListTest_Modify();
return 0;
}
测试功能:
![](https://i-blog.csdnimg.cn/blog_migrate/741d0fcd1584291bf109f67bcbefba27.png)
二、顺序表程序:
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListTest_Back()
{
SeqList SL;
SeqListInit(&SL);
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 4);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
SeqListPopBack(&SL);
SeqListPrint(&SL);
}
void SeqListTest_Front()
{
SeqList SL;
SeqListInit(&SL);
SeqListPushFront(&SL, 5);
SeqListPushFront(&SL, 4);
SeqListPushFront(&SL, 3);
SeqListPushFront(&SL, 2);
SeqListPushFront(&SL, 1);
SeqListPushFront(&SL, 0);
SeqListPrint(&SL);
SeqListPopFront(&SL);
SeqListPrint(&SL);
}
void SeqListTest_Insert_Earse()
{
SeqList SL;
SeqListInit(&SL);
//尾插填入数据
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
//测试
SeqListInsert(&SL, 3, 4);
SeqListInsert(&SL, 5, 6);
SeqListPrint(&SL);
SeqListErase(&SL, 5);
SeqListPrint(&SL);
}
void SeqListTest_Find()
{
SeqList SL;
SeqListInit(&SL);
//尾插填入数据
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 4);
SeqListPushBack(&SL, 5);
SeqListPrint(&SL);
//查找顺序表中的5并删除
int ret = SeqListFind(&SL, 5);
if (ret != -1)
{
SeqListErase(&SL, ret);
}
SeqListPrint(&SL);
}
void SeqListTest_Modify()
{
SeqList SL;
SeqListInit(&SL);
//尾插填入数据
SeqListPushBack(&SL, 1);
SeqListPushBack(&SL, 2);
SeqListPushBack(&SL, 3);
SeqListPushBack(&SL, 4);
SeqListPushBack(&SL, 5);
SeqListPushBack(&SL, 0);
SeqListPrint(&SL);
SeqListModify(&SL, 5, 6);
SeqListPrint(&SL);
}
int main()
{
//printf("尾插尾删测试:\n");
//SeqListTest_Back();
/*printf("头插头删测试:\n");
SeqListTest_Front();*/
//printf("指定位置插入删除测试:\n");
//SeqListTest_Insert_Earse();
//printf("查找数据测试:\n");
//SeqListTest_Find();
printf("测试修改指定位置数据功能:\n");
SeqListTest_Modify();
return 0;
}
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
void SeqListInit(SeqList* ps)
{
assert(ps);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
//释放
void SeqListDestroy(SeqList* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
}
//打印
void SeqListPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//检查容量并扩容
void CheckCapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
DataType* ptr = (DataType*)realloc(ps->a, sizeof(DataType) * newcapacity);
if (ptr == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = ptr;
ps->capacity = newcapacity;
}
}
//尾插
void SeqListPushBack(SeqList* ps, DataType x)
{
//assert(ps);
//CheckCapacity(ps);
//ps->a[ps->size] = x;
//ps->size++;
SeqListInsert(ps, ps->size, x);
}
//尾删
void SeqListPopBack(SeqList* ps)
{
//assert(ps);
//assert(ps->size > 0);
//ps->size--;
SeqListErase(ps, ps->size-1);
}
//头插
void SeqListPushFront(SeqList* ps, DataType x)
{
//assert(ps);
//CheckCapacity(ps);
挪动数据
//int end = ps->size-1;
//while (end >= 0)
//{
// ps->a[end + 1] = ps->a[end];
// end--;
//}
//ps->a[0] = x;
//ps -> size++;
SeqListInsert(ps, 0, x);
}
//头删
void SeqListPopFront(SeqList* ps)
{
//assert(ps);
//assert(ps->size > 0);
挪动数据
//int begin = 1;
//while (begin < ps->size)
//{
// ps->a[begin - 1] = ps->a[begin];
// begin++;
//}
//ps->size--;
SeqListErase(ps, 0);
}
//在pos位置插入数据
void SeqListInsert(SeqList* ps, int pos, DataType x)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
CheckCapacity(ps);
//挪动数据
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
//删除pos位置的数据
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
//顺序表查找
int SeqListFind(SeqList* ps, DataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
//修改指定下表的数据
void SeqListModify(SeqList* ps, int pos, DataType x)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
ps->a[pos] = x;
}
SeqList.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;
typedef struct SeqList
{
DataType* a;
int size;
int capacity;
}SeqList;
//初始化
void SeqListInit(SeqList* ps);
//释放
void SeqListDestroy(SeqList* ps);
//打印
void SeqListPrint(SeqList* ps);
//尾插
void SeqListPushBack(SeqList* ps,DataType x);
//尾删
void SeqListPopBack(SeqList* ps);
//头插
void SeqListPushFront(SeqList* ps, DataType x);
//头删
void SeqListPopFront(SeqList* ps);
//中间插入
void SeqListInsert(SeqList* ps, int pos, DataType x);
//中间删除
void SeqListErase(SeqList* ps, int pos);
//顺序表查找
int SeqListFind(SeqList* ps, DataType x);
//修改指定下表的数据
void SeqListModify(SeqList* ps, int pos, DataType x);
三、OJ练习:
1.原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。
int removeElement(int* nums, int numsSize, int val)
{
int dst = 0, src = 0;
while(src<numsSize)
{
if(nums[src] == val)
{
src++;
}
else
{
nums[dst] = nums[src];
dst++;
src++;
}
}
return dst;
}
采用"双指针"的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/0d4cf138deeac88911e9c95d4cb79a8d.png)
2.删除排序数组中的重复项。
int removeDuplicates(int* nums, int numsSize)
{
int dst = 0, src = 0;
while(src<numsSize)
{
if(nums[src] == nums[dst])
{
src++;
}
else
{
dst++;
nums[dst] = nums[src];
src++;
}
}
return dst+1;
}
当两个数据相等时,src指针跳过一个元素,不相等时,dst指针先++,然后dst指针位置的元素等于src位置的元素。
![](https://i-blog.csdnimg.cn/blog_migrate/f4c9f02d122d6db1bd60cb680749b243.png)
3.合并连个有序数组。
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int i1 = m-1, i2 = n-1;
int j = m+n-1;
while(i1 >= 0 && i2 >= 0)
{
if(nums1[i1] < nums2[i2])
{
nums1[j--] = nums2[i2--];
}
else
{
nums1[j--] = nums1[i1--];
}
}
while(i2 >= 0)
{
nums1[j--] = nums2[i2--];
}
}
一共设置三个指针,且三个指针的位置都在数组的尾部,原因是设置在头部可能会导致数据覆盖错误。
此题要求将两个数组合并到第一个数组中,当第一个指针走完,第二个没走完,即是第一个数组的元素已比较完并覆盖完毕,第二个数组中仍有元素未覆盖,第二个循环即将第二个数组中的元素覆盖完。
![](https://i-blog.csdnimg.cn/blog_migrate/6fca575478d119b16243629e9a86e9c3.png)