1.顺序表的实现
//#include<stdio.h>
//#define N 100
//typedef int SLDataType;
//
////静态顺序表
//typedef struct SeqList
//{
// SLDataType a[N];
// int size; //表示数组中存储了多少个数组
//
// }SL;
//
//
////接口函数 --命名风格是跟着STL走的
//void SeqListInit(SL* ps); //初始化
//
////静态表特点:如果满了就不让插入 缺点:很难确定多少空间合适
//void SeqListPushBack(SL* ps,SLDataType x); //尾插
//void SeqListPopBack(SL* ps); //尾删
//void SeqListPushFront(SL* ps,SLDataType x); //头插
//void SeqListPoPFront(SL* ps); //头删
////……
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
SLDataType* a;
int size; //表示数组中存储了多少个数据
int capacity; //表示数组实际能存数据的容量空间是多少 (单位是数据个数)
}SL;
//打印函数
void SeqListPrint(SL* ps)
{
for(int i=0;i<ps->size;i++)
{
printf("%d ",ps->a[i]);
}
printf("\n");
}
//接口函数 --命名风格是跟着STL走的
void SeqListInit(SL* ps) //初始化
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
//销毁顺序表
void SeqListDerstory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity=ps->size=0;
}
void SeqListCheckCapacity(SL* ps)
{
//如果没有空间或空间不足,那么我们就扩容
//realloc函数返回的是void*类型的指针
//第一个参数是先前开辟内存块的指针,第二个参数是新的字节数,而不是新增的字节数
//使用realloc函数的方法是:一个新的指针变量=(数据类型*)realloc(先前指针,新字节数)
if(ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0?4: ps->capacity*2;
SLDataType* tmp = (SLDataType*)realloc(ps->a,newcapacity*sizeof(SLDataType));
if(tmp==NULL)
{
printf("realloc fail\n");
exit(-1); //退出函数 异常退出
}
ps->a=tmp;
ps->capacity=newcapacity;
}
}
void SeqListPushBack(SL* ps,SLDataType x) //尾插 尾部进行数据
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SeqListPopBack(SL* ps) //尾删
{
// //防止越界
// //方法一 温柔处理
// if(ps->size>0)
// {
// //ps->a[ps->size-1] = 0; 不影响
// ps->size--;
// }
//方法二 暴力处理
assert(ps->size>0); //断言 如果小于或等于0就终止程序,并告知我们断言失败
ps->size--;
}
void SeqListPushFront(SL* ps,SLDataType x) //头插
{
SeqListCheckCapacity(ps);
//挪动数据
int end = ps->size-1;
while(end>=0)
{
ps->a[end+1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
void SeqListPoPFront(SL* ps) //头删
{
assert(ps->size>0);
int begin = 1;
while(begin < ps->size)
{
ps->a[begin-1] = ps->a[begin];
++begin;
}
ps->size--;
}
//查找这个值的位置下标,没有找到返回-1
int SeqListFind(SL* ps,SLDataType x)
{
for(int i=0;i<ps->size;i++)
{
if(ps->a[i]==x)
{
return i;
}
}
return -1;
}
//在pos指定位置插入X
void SeqListInsert(SL* ps,int pos,SLDataType x)
{
// //温柔
// if(pos>ps->size||pos<0)
// {
// printf("pos invalid\n");
// return;
// }
//暴力的方式
assert(pos<=ps->size&&pos>=0);
SeqListCheckCapacity(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(SL* ps,int pos)
{
//暴力的方式
assert(pos<ps->size&&pos>=0);
int begin = pos;
while(begin<ps->size)
{
ps->a[begin]=ps->a[begin+1];
++begin;
}
--ps->size;
}
//……
void TestSeqList1()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1,1);
SeqListPushBack(&s1,2);
SeqListPushBack(&s1,3);
SeqListPushBack(&s1,4);
SeqListPushBack(&s1,5);
SeqListPrint(&s1);
SeqListPopBack(&s1);
SeqListPrint(&s1);
SeqListDerstory(&s1);
}
void TestSeqList2()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1,1);
SeqListPushBack(&s1,2);
SeqListPushBack(&s1,3);
SeqListPushBack(&s1,4);
SeqListPushBack(&s1,5);
SeqListPrint(&s1);
SeqListPushFront(&s1,10);
SeqListPushFront(&s1,20);
SeqListPushFront(&s1,30);
SeqListPushFront(&s1,40);
SeqListPrint(&s1);
SeqListPoPFront(&s1);
SeqListPrint(&s1);
SeqListDerstory(&s1);
}
void TestSeqList3()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1,1);
SeqListPushBack(&s1,2);
SeqListPushBack(&s1,3);
SeqListPushBack(&s1,4);
SeqListPushBack(&s1,5);
SeqListPrint(&s1);
printf("%d\n",SeqListFind(&s1,3));
printf("%d\n",SeqListFind(&s1,0));
SeqListInsert(&s1,2,0);
SeqListPrint(&s1);
SeqListErase(&s1,3);
SeqListPrint(&s1);
SeqListDerstory(&s1);
}
int main()
{
//TestSeqList1();
//TestSeqList2();
TestSeqList3();
return 0;
}
2.相关练习
(1)有一个数组和一个值,原地移除所有数值等于这个数的元素,并返回移除后新数组的长度
#include<stdio.h>
//给定数组nums和一个值val
//思路1:找到所有的val,一次挪动数据覆盖删除
// 时间复杂度O(N*2) 最坏的情况:数组中大部分值甚至全部都是val
// (n-1)+(n-2)+(n-3)+……
//思路2:一次遍历nums数组,把不是val的值,放到tmp数组,再把tmp数组的值拷贝回去
// 时间复杂度O(N) 空间复杂度O(N) 空间换时间
//思路3: src去找nums数组中不等于val的值,放到dst指向的位置上去,再++src,++dst
//双指针
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];
++src;
++dst;
}
else
{
++src;
}
}
return dst;
}
int main()
{
int nums[5]={1,3,4,3,5};
int val=3;
printf("%d\n",removeElement(nums,5,val));
return 0;
}
(2)原地删除重复元素,使每个元素只出现一次,返回删除后数组长度
//去重
#include<stdio.h>
int removeDuplicates(int* nums,int numsSize)
{
if(numsSize==0)
{
return 0;
}
int i = 0;
int j = 1;
int dst = 0;
while(j<numsSize)
{
if(nums[i]==nums[j])
{
++j;
}
else
{
nums[dst]=nums[i];
++dst;
i = j;
++j;
}
}
nums[dst]=nums[i];
++dst;
return dst;
}
int main()
{
int nums[]={0,0,1,1,1,2,2,3,3,4};
printf("%d\n",removeDuplicates(nums,10));
return 0;
}
3.顺序表优缺点
顺序表缺陷:
- 空间不够需要增容,增容需要代价
增容分为:①原地扩
②异地扩
- 避免频繁扩容,我们满了基本都是扩二倍,可能就会导致一定的空间浪费。
- 顺序表要求数据从开始位置连续存储,那么我们在头部或者中间位置插入或删除数据就需要挪动数据,效率不高。
- 针对顺序表缺陷,设计出链表。
优点:
支持随机访问
有些算法,需要结构支持随机访问,比如二分查找、优化的快排等