C语言顺序表:从原理到实战优化

C语言顺序表技术详解:从原理到实现

一、引言

         线性表(Linear List)是最基本、最简单且最常用的一种数据结构

                     有序性:元素之间存在严格的顺序关系

                     同类型:所有元素属于同一数据类型

                    分类:1. 顺序表

                               2.链表

  1. 线性表的概念与分类
  2. 顺序表的定义与核心特性
    • 连续内存存储
    • 随机访问能力 O(1)
    • 顺序表在物理上和逻辑上都是连续的
  3. 适用场景分析
    • 高频查询 and 低频插入/删除
二、顺序表的结构设计
  1. 静态分配实现
    #define MAX_SIZE 100
    typedef struct  SeqList
    {
        int data[MAX_SIZE]; 
        int length; 
    } SeqList;
    

  2. 动态分配实现(核心优势)
    typedef int SLDataType;
    
    typedef struct SequenceList
    {
    	SLDataType* arr;
    	int size;
    	int capasity;
    }SL;

  3. 关键参数说明

              静态分配实现存在局限性,如空间浪费或空间开辟不足的情况

              动态分配则可以灵活的开辟空间,根据需要分配容量大小

    • capacity vs max_size 的区别  
    • 内存布局示意图

                     

三、核心操作实现

  1. 初始化与销毁
    // 动态初始化
    void SequenceList_Init(SL *ps)
    {
    	ps->arr = NULL;
    	ps->size = 0;
    	ps->capasity = 0;
    }

  2. 插入操作
    • 时间复杂度分析: O(1) (尾部添加), O(n) (头部添加)
    • 边界处理:位置合法性检查
    //判断顺序表是否开辟空间
    void SequenceList_Checkcapasity(SL *ps)
    {
    	int newcapasity;
    	if (ps->capasity == ps->size)
    	{
    		newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;
    		SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType));
    		if (temp == NULL)
    		{
    			perror("realloc fail!");
    			exit(1);
    		}
    		ps->arr = temp;
    		ps->capasity = newcapasity;
    	}
    }
    
    
    //头部添加
    void SequenceList_PushFront(SL* ps, SLDataType x)
    {
    	assert(ps);
    	SequenceList_Checkcapasity(ps);
    	int i;
    	for (i = ps->size; i>0; i--)
    	{
    		ps->arr[i] = ps->arr[i-1];         //arr[1] = arr[0],用来判断i的截止条件
    	}
    	ps->arr[0] = x;
    	ps->size++;
    }
    
    
    //尾部添加
    void SequenceList_PushBack(SL* ps, SLDataType x)
    {
    	assert(ps);
    	SequenceList_Checkcapasity(ps);
    	ps->arr[(ps->size)++] = x;
    }
    
    
    //指定地方添加
    void SequenceList_Insert(SL* ps,int pos, SLDataType x)
    {
    	assert(ps);
    	assert(pos <= ps->size && pos >= 0); //位置合法性检查
    	SequenceList_Checkcapasity(ps);
    	
    	int i;
    
    	for (i = ps->size; i>pos; i--)
    	{
    		ps->arr[i] = ps->arr[i - 1];  //arr[pos+1] = arr[pos],用来判断i的截止条件
    	}
    	ps->arr[pos] = x;
    	ps->size++;
    }

  3. 删除操作
    • //头部删除
      void SequenceList_PopFront(SL* ps)
      {
      	assert(ps);
      	int i;
      	for (i = 0; i<ps->size -1; i++)
      	{
      		ps->arr[i] = ps->arr[i + 1];        //arr[size-2] = arr[size-1]
      	}
      	ps->size--;
      }
      
      
      //尾部删除
      void SequenceList_PopBack(SL* ps)
      {
      	assert(ps);
      	(ps->size)--;
      }
      
      
      //指定地方删除
      void SequenceList_Erase(SL* ps, int pos)
      {
      	assert(ps);
      	assert(pos <= ps->size && pos >= 0);
      	int i;
      	for (i = pos; i<ps->size-1; i++)
      	{
      		ps->arr[i] = ps->arr[i + 1];  //arr[size-2] = arr[size-1]
      	}
      	ps->size--;
      }

  4. 动态扩容策略
    //判断顺序表是否开辟空间
    void SequenceList_Checkcapasity(SL *ps)
    {
    	int newcapasity;
    	if (ps->capasity == ps->size)
    	{
    		newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;
    		SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType));
    		if (temp == NULL)
    		{
    			perror("realloc fail!");
    			exit(1);
    		}
    		ps->arr = temp;
    		ps->capasity = newcapasity;
    	}
    }
    

四、性能与优化
  1. 时间复杂度对比表
    操作平均时间复杂度
    随机访问O(1)
    尾部插入O(1)
    指定位置插入O(n)
    头部插入O(n)
  2. 批量操作优化策略

             newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;

            如果原先没有容量,则会先开辟一个大小为4个数据大小的空间

    • 预分配机制

五、总结
  1. 顺序表的三大优势

            由于缓存的速度相较于内存更快,所以cpu会将内存中的内容加载至缓存中再进行访问

            若原先内容已经存在于缓存中,则直接访问,称为缓存命中

            若原先内容不存在于缓存中,需要加载至缓存中再访问,称为不命中

             由于缓存加载会将一块连续的区域加载至缓存,所以顺序表有CPU缓存友好性

           

              直接通过下标即可访问

    • CPU缓存友好性:
    • 访问效率确定性
    • 实现简单性
  2. 典型使用误区警示
    • 避免中间频繁插入:效率低下
代码总览
Sequence.h   
pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;

typedef struct SequenceList
{
	SLDataType* arr;
	int size;
	int capasity;
}SL;


void SequenceList_Checkcapasity(SL* ps);//判断顺序表是否开辟空间
void SequenceList_Init(SL* ps);//初始化顺序表
void SequenceList_Print(SL* ps);//打印顺序表
void SequenceList_Destroy(SL* ps);//销毁顺序表
void SequenceList_PushFront(SL* ps, SLDataType x);//头部添加
void SequenceList_PushBack(SL* ps, SLDataType x);//尾部添加
void SequenceList_PopFront(SL* ps);//头部删除
void SequenceList_PopBack(SL* ps);//尾部删除
void SequenceList_Insert(SL* ps, int pos, SLDataType x);//指定地方添加
void SequenceList_Erase(SL* ps, int pos);//指定地方删除




Sequence.c
#define _CRT_SECURE_NO_WARNINGS
#include "Sequence.h"

//判断顺序表是否开辟空间
void SequenceList_Checkcapasity(SL *ps)
{
	int newcapasity;
	if (ps->capasity == ps->size)
	{
		newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;
		SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType));
		if (temp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = temp;
		ps->capasity = newcapasity;
	}
}

//初始化顺序表
void SequenceList_Init(SL *ps)
{
	ps->arr = NULL;
	ps->size = 0;
	ps->capasity = 0;
}

//打印顺序表
void SequenceList_Print(SL* ps)
{
	assert(ps);
	int i;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

//销毁顺序表
void SequenceList_Destroy(SL* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->capasity = ps->size = 0;
	
}

//头部添加
void SequenceList_PushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SequenceList_Checkcapasity(ps);
	int i;
	for (i = ps->size; i>0; i--)
	{
		ps->arr[i] = ps->arr[i-1];         //arr[1] = arr[0]
	}
	ps->arr[0] = x;
	ps->size++;
}

//尾部添加
void SequenceList_PushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SequenceList_Checkcapasity(ps);
	ps->arr[(ps->size)++] = x;
}

//头部删除
void SequenceList_PopFront(SL* ps)
{
	assert(ps);
	int i;
	for (i = 0; i<ps->size -1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];        //arr[size-2] = arr[size-1]
	}
	ps->size--;
}

//尾部删除
void SequenceList_PopBack(SL* ps)
{
	assert(ps);
	(ps->size)--;
}


//指定地方添加
void SequenceList_Insert(SL* ps,int pos, SLDataType x)
{
	assert(ps);
	assert(pos <= ps->size && pos >= 0);
	SequenceList_Checkcapasity(ps);
	
	int i;

	for (i = ps->size; i>pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];  //arr[pos+1] = arr[pos]
	}
	ps->arr[pos] = x;
	ps->size++;
}

//指定地方删除
void SequenceList_Erase(SL* ps, int pos)
{
	assert(ps);
	assert(pos <= ps->size && pos >= 0);
	int i;
	for (i = pos; i<ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];  //arr[size-2] = arr[size-1]
	}
	ps->size--;
}





test.c
#define _CRT_SECURE_NO_WARNINGS
#include "Sequence.h"

void Test01()
{
	SL s;
	SequenceList_Init(&s);

	//尾部添加5个数据
	SequenceList_PushBack(&s,11);
	SequenceList_PushBack(&s, 22);
	SequenceList_PushBack(&s, 33);
	SequenceList_PushBack(&s, 44);
	SequenceList_PushBack(&s, 55);

	//头部添加1个数据
	SequenceList_PushFront(&s, 66);

	//打印顺序表
	SequenceList_Print(&s);

	//头部删除2个数据
	SequenceList_PopFront(&s);
	SequenceList_PopFront(&s);

	SequenceList_Print(&s);

	//指定地方添加一个数据
	SequenceList_Insert(&s, 2, 77);

	//打印顺序表
	SequenceList_Print(&s);

	//指定地方删除一个数据
	SequenceList_Erase(&s, 2);

	//打印顺序表
	SequenceList_Print(&s);

	//销毁顺序表
	SequenceList_Destroy(&s);
}

int main()
{
	Test01();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值