线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
静态顺序表:使用定长数组存储。
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size; // 有效数据的个数
}SeqList;
动态顺序表:使用动态开辟的数组存储。
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
接口实现
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
//动态顺序表
typedef int DataType;
typedef struct seqList
{
DataType* _array;
size_t _size; //元素个数
size_t _capacity; //容量
}seqList;
//初始化数组
void seqListInit(seqList* sl)
{
sl->_array = (int*)malloc(sizeof(DataType) * 4);
sl->_capacity = 4;
sl->_size = 0;
}
//增容
void checkCapacity(seqList* sl)
{
if (sl->_size == sl->_capacity)
{
sl->_capacity *= 2;
sl->_array = (DataType*)realloc(sl->_array, sizeof(DataType) * sl->_capacity);
}
}
// 再pop位置面前插入一个数据value
void seqListInsert(seqList * sl, size_t pos, DataType value)
{
if (pos <= sl->_size)
{
checkCapacity(sl);
size_t end = sl->_size;
while (end > pos)
{
sl->_array[end] = sl->_array[end - 1];
--end;
}
sl->_array[pos] = value;
++sl->_size;
}
}
//删除pos位置的数据
void seqListErase(seqList* sl, size_t pos)
{
if (pos < sl->_size)
{
size_t start = pos + 1;
while (start < sl->_size)
{
sl->_array[start - 1] = sl->_array[start];
++start;
}
--sl->_size;
}
}
//pushBack 尾插
void seqListPushBack(seqList* sl, DataType value)
{
seqListInsert(sl,sl->_size,value);
}
//PopBack 尾删
void seqListPopBack(seqList* sl)
{
seqListErase(sl, sl->_size - 1);
}
//PushFront 头插
void seqListPushFront(seqList* sl, DataType value)
{
seqListInsert(sl, 0, value);
}
//PopFront头删
void seqListPopFront(seqList* sl)
{
seqListErase(sl, 0);
}
int seqListFind(seqList* sl, DataType value)
{
for (int i = 0; i<sl->_size; ++i)
{
if (sl->_array[i] == value)
{
return i;
}
}
return -1;
}
void seqListPrint(seqList* sl)
{
for (size_t i = 0; i < sl->_size; ++i)
{
printf("%d",sl->_array[i]);
}
printf("\n");
}
int removeElement(int* nums, int numsSize, int val)
{
//int* newA = (int*)mealloc(sizeof(numsSize * sizeof(int)));
int idx = 0;
for (int i = 0; i < numsSize; ++i)
{
if (nums[i] != val)
nums[idx++] = nums[i];
}
return idx;
}
链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
单向、双向 —— 带头、不带头 —— 循环、非循环
常用的两种:
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。
#include<stdio.h>
#pragma once;
typedef int Type;
typedef struct Node
{
struct Node* _next;
Type _data;
}Node;
//实现不带头单向非循环链表
typedef struct SingleList
{
Node* _head;//表示链表真正的头节点,即第一个有效的数据的位置
}SingleList;
void singleListInit(SingleList* sl)
{
//空链表
sl->_head = NULL;
}
Node* creatNode(Type data)
{
Node* node = (Node*)malloc(sizeof(Node));
node->_data = data;
node->_next = NULL;
return node;
}
void singleListPrint(SingleList* sl)
{
Node* cur = sl->_head;
while (cur)
{
printf("%d", cur->_data);
cur = cur->_next;
}
printf("\n");
}
void singleListPushFront(SingleList* sl, Type data)
{
Node* node = creatNode(data);
//是否为空表
if (sl->_head == NULL)
{
sl->_head = node;
}
else {
node->_next = sl->_head;
sl->_head = node;
}
}
void singleListPopFront(SingleList* sl)
{
if (sl->_head)
{
Node* cur = sl->_head;
sl->_head = cur->_next;
free(cur);
}
}
void singleListPushBack(SingleList* sl, Type data)
{
Node* node = creatNode(data);
if (sl->_head == NULL)
sl->_head = node;
else {
//找到最后一个数据
Node* last = sl->_head;
while (last->_next)
last = last->_next;
last->_next = node;
}
}
void singleListPopBack(SingleList* sl)
{
//找到最后一个节点,并且,修改被删除的节点的前驱节点的执行
if (sl->_head)
{
//找到最后一个节点,遍历的过程中,更新prev
Node* prev = NULL;
Node* tail = sl->_head;
while (tail->_next)
{
prev = tail;
tail = tail->_next;
}
if (tail == sl->_head)
sl->_head = NULL;
else
prev->_next = NULL;
free(tail);
}
}
void SingListDestory(SingList* Sl) {
Node* cur = Sl->head;
//创建指针指向链表头
while (cur) {
Node* next = cur->next;
//先保存其中指向下一个节点的地址
free(cur);
cur = next;
//将保存下来的节点位置再次赋给cur指针
}
}
顺序表和链表的区别和联系
顺序表
优点:空间连续、支持随机访问。
缺点:中间或前面部分的插入删除时间复杂度O(N) ,增容的代价比较大。
链表
优点:任意位置插入删除时间复杂度为O(1) ,没有增容问题,插入一个开辟一个空间。
缺点:以节点为单位存储,不支持随机访问。