线性表
线性表按照存储方式分为两种:顺序表和链表
顺序表的知识点
顺序表:结点与结点之间,逻辑相邻,物理也相邻
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表:可动态增长的数组,要求数据是连续存储的
顺序表特点
顺序表的操作需要挪动元素,插入的话,需要向后挪动元素,删除的话需要向前覆盖,时间复杂度O(n)效率不高,但是如果在尾部操作则效率极高,为O(1),因为尾部操作不需要挪动元素
尾插,不需要挪动元素
尾删,不需要向前覆盖
以上两种操作效率极高,为O(1)
顺序表的使用场景:
适合的场景:支持直接访问,插入和删除操作较多
不适合的场景:在头部和中间位置,删除和插入操作较多,频繁扩容,扩容代价高
增删改查思路
每种均举一种做示例,便可以有其他位置插入删除的思路,除插入删除初始化之外的函数代码思路很明确,在此处不做思路讲解,
初始化
动态申请顺序表长度大小的内存
设置有效长度和顺序表的长度
插入
头插:
1.判断指针sq是否为NULL
2.判满,满了则扩容
3.向后挪动元素
4.插入有效值val
5.length+1
删除
按位置删:
1.安全性处理 判空(判断sq指针不指向NULL)
2.判断pos的合法性 pos默认为0时,为头删
3.判空(判断sq指向的是否是一个空的顺序表)
4:向前覆盖
5:length不要忘记-1
结构体及函数声明
#pragma once
//可扩容的顺序表的结构体设计
#define LIST_INIT_SIZE 100 //初始化顺序表长度
#define LISTINCREMENT 10 //扩容
typedef int ELEM_TYPE;
typedef struct Sqlist {
ELEM_TYPE *elem;
int length;
int listsize;
}Sqlist,*PSqlist;
//关于数据结构的操作,无外乎就是增删改查
//初始化
void Init_Sqlist(struct Sqlist* sq);
//头插
bool Insert_head(struct Sqlist* sq, ELEM_TYPE val);
//尾插
bool Insert_tail(struct Sqlist* sq, ELEM_TYPE val);
//按位置插
bool Insert_pos(struct Sqlist* sq, int pos, ELEM_TYPE val);
//头删
bool Del_head(struct Sqlist* sq);
//尾删
bool Del_tail(struct Sqlist* sq);
//按位置删
bool Del_pos(struct Sqlist* sq, int pos);
//按值删(找这个值在顺序表中,第一次出现的位置,然后删除掉)
bool Del_val(struct Sqlist* sq, ELEM_TYPE val);
//判空
bool IsEmpty(struct Sqlist* sq);
//判满
bool IsFull(struct Sqlist* sq);
//扩容函数
void Inc(struct Sqlist* sq);
//查找(找这个值在顺序表中,第一次出现的位置)
int Search(struct Sqlist* sq, ELEM_TYPE val);
//清空(将数据清空,认为没有有效值)
void Clear(struct Sqlist* sq);
//销毁(将数据存储空间都释放掉)
void Destory(struct Sqlist* sq);
//打印
void Show(struct Sqlist* sq);
函数实现
#include <stdio.h>
#include <assert.h>
//#include <malloc.h>
#include <stdlib.h>
#include "sqlist.h"
#include<stdio.h>
#include<vld.h>
//初始化 对头结点里面的成员赋值
void Init_Sqlist(struct Sqlist* sq)
{
sq->elem = (ELEM_TYPE*)malloc(LIST_INIT_SIZE * sizeof(int));
assert(sq->elem != NULL);
sq->length = 0;
sq->listsize = LIST_INIT_SIZE;
}
//头插
bool Insert_head(struct Sqlist* sq, ELEM_TYPE val)
{
//1.判断指针sq是否为NULL
assert(sq != NULL);
//2.判满,满了则扩容
if (IsFull(sq))
{
Inc(sq);
}
//3.向后挪动元素
for (int i = sq->length - 1; i >= 0; i--)
{
sq->elem[i + 1] = sq->elem[i];
}
//4.插入有效值val
sq->elem[0] = val;
//5.length+1
sq->length++;
return true;
}
//尾插
bool Insert_tail(struct Sqlist* sq, ELEM_TYPE val)
{
//1.判断指针sq是否为NULL
assert(sq != NULL);
//2.判满,满了则扩容
if (IsFull(sq))
{
Inc(sq);
}
//3.插入有效值val
sq->elem[sq->length] = val;
//4.length+1
sq->length++;
return true;
}
//按位置插
bool Insert_pos(struct Sqlist* sq, int pos, ELEM_TYPE val)
{
//将插入的位置元素和后面的元素,整体向后挪动
//1.判断指针sq是否为NULL
assert(sq != NULL);
//2.判满,满了则扩容
if (IsFull(sq))
{
Inc(sq);
}
//3.向后挪动元素
for (int i = sq->length - 1; i >= pos; i--)
{
sq->elem[i + 1] = sq->elem[i];
}
//4.将val值,放入
sq->elem[pos] = val;
//5.length+1
sq->length++;
return true;
}
//头删
bool Del_head(struct Sqlist* sq)
{
//1插入需要判满 删除也需要判空
if (IsEmpty(sq))
{
return false;
}
//上面if没成功,则代表有数据
//2.待删除元素,后面的元素向前覆盖
for (int i = 1; i < sq->length; i++)
{
sq->elem[i - 1] = sq->elem[i];
}
//3.有效长度-1
sq->length--;
return true;
}
//尾删
bool Del_tail(struct Sqlist* sq)
{
//1.安全性处理 判空(判断sq指针不指向NULL)
assert(sq != NULL);
//2.判空(判断sq指向的是否是一个空的顺序表)
if (IsEmpty(sq))
{
return false;
}
//3.删除
sq->length--;
return true;
}
//按位置删
bool Del_pos(struct Sqlist* sq, int pos)
{
//1.安全性处理 判空(判断sq指针不指向NULL)
assert(sq != NULL);
//2.判断pos的合法性 pos默认为0时,为头删
assert(pos >= 0 && pos < sq->length);
//3.判空(判断sq指向的是否是一个空的顺序表)
if (IsEmpty(sq))
{
return false;
}
//4:向前覆盖
假设让i指向被覆盖者(被动),则代码为:
//for(int i=pos; i<sq->length-1; i++)
//{
// sq->elem[i] = sq->elem[i+1];
//}
//假设让i指向覆盖者(主动),则代码为:
for (int i = pos + 1; i < sq->length; i++)
{
sq->elem[i - 1] = sq->elem[i];
}
//5:length不要忘记-1
sq->length--;
return true;
}
//按值删(找这个值在顺序表中,第一次出现的位置,然后删除掉)
bool Del_val(struct Sqlist* sq, ELEM_TYPE val)
{
//assert sq
int tmp = Search(sq, val);
//如果找到了,则tmp保存着这个值所在的下标
//如果没找到,则tmp为-1
if (tmp == -1)
{
return false;
}
//如果代码执行到这一行,则代表tmp不等于-1,表示这个值存在,且它的下标为tmp保存
return Del_pos(sq, tmp);
}
//查找(找这个值在顺序表中,第一次出现的位置)
int Search(struct Sqlist* sq, ELEM_TYPE val)
{
//assert sq
for (int i = 0; i < sq->length; i++)
{
if (sq->elem[i] == val)
{
return i;
}
}
return -1;
}
//判空
bool IsEmpty(struct Sqlist* sq)
{
//asseret
return sq->length == 0;
}
//判满
bool IsFull(struct Sqlist* sq)
{
//assert
return sq->length == sq->listsize;
}
//扩容函数 //按2倍扩容
void Inc(struct Sqlist* sq)
{
ELEM_TYPE* p = (ELEM_TYPE *)realloc(sq->elem, (sq->listsize * 2)* sizeof(int) );
sq->elem = p;
assert(sq->elem != NULL);
//sq->length; 不需要修改
sq->listsize *= 2;
}
//清空(将数据清空,认为没有有效值)
void Clear(struct Sqlist* sq)
{
sq->length = 0;
}
//销毁(将数据存储空间都释放掉)
void Destory(struct Sqlist* sq)
{
free(sq->elem);
sq->length = sq->listsize = 0;
}
//打印
void Show(struct Sqlist* sq)
{
for (int i = 0; i < sq->length; i++)
{
printf("%d ", sq->elem[i]);
}
printf("\n");
}
顺序表与单链表的区别