1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
2.1概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素
2. 动态顺序表:使用动态开辟的数组存储
2.2 接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空
间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间
大小,所以下面我们实现动态顺序表。
#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
int main()
{
SLTNode* plist = NULL;
SLTPushFront(&plist,1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
//SLTPushBack(&plist, 4);
//SLTPopFront(&plist);
//SLTPopBack(&plist);
SLTNode* pos = STFind(plist, 3);
if (pos)
{
SLInsert(&plist, pos, 30);
}
pos = STFind(plist, 2);
if (pos)
{
SLErase(&plist, pos);
}
pos = STFind(plist, 30);
if (pos)
{
SLEraseAfter(pos);
}
SLTprint(plist);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDatatype;
typedef struct SListNode
{
SLTDatatype data;
struct SListNode* next;
}SLTNode;
void SLTprint(SLTNode* phead);
void SLTPushFront(SLTNode**pphead, SLTDatatype x);
void SLTPushBack(SLTNode** pphead, SLTDatatype x);
void SLTPopFront(SLTNode** pphead);
void SLTPopBack(SLTNode** pphead);
SLTNode* STFind(SLTNode* plist, SLTDatatype x);
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x);
void SLInsertAfter( SLTNode* pos, SLTDatatype x);
void SLErase(SLTNode** pphead, SLTNode* pos);
void SLEraseAfter(SLTNode* pos);
#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
void SLTprint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuyTNode(SLTDatatype x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushFront(SLTNode** pphead, SLTDatatype x)
{
SLTNode* newnode = BuyTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDatatype x)
{
SLTNode* newnode = BuyTNode(x);
SLTNode* tail = *pphead;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
void SLTPopFront(SLTNode** pphead)
{
SLTNode* tail = *pphead;
*pphead = tail->next;
free(tail);
tail = NULL;
}
void SLTPopBack(SLTNode** pphead)
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
SLTNode* STFind(SLTNode* plist, SLTDatatype x)
{
SLTNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuyTNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
void SLInsertAfter(SLTNode* pos, SLTDatatype x)
{
assert(pos);
SLTNode* newnode = BuyTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SLErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == NULL)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
void SLEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
3.链表
3.1 链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
3.5 双向链表的实现
// 2、带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* plist);
// 双向链表打印
void ListPrint(ListNode* plist);
// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* plist);
// 双向链表头插
void ListPushFront(ListNode* plist, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* plist);
// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的结点
void ListErase(ListNode* pos);
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
void Testlist1()
{
LTNode* plist = LTInit();
LTPushBack(plist,1);
LTPushBack(plist, 1);
LTPushBack(plist, 1);
LTPushFront(plist,2);
LTPopBack(plist);
LTPopFront(plist);
LTNode* pos = LTFind(plist,2);
if (pos)
{
LTInsert(pos, 20);
}
pos = LTFind(plist, 2);
if (pos)
{
LTInsert(pos, 20);
}
LTPrint(plist);
LTDestroy(plist);
plist = NULL;
}
int main()
{
Testlist1();
return 0;
}
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LTDataType data;
}LTNode;
LTNode* LTInit();
void LTPrint(LTNode*phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
LTNode* LTInit()
{
LTNode* phead = BuyLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyLTNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyLTNode(x);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
phead->prev = tailprev;
tailprev->next = phead;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->next;
LTNode* tailnext = tail->next;
free(tail);
phead->next = tailnext;
tailnext->prev = phead;
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyLTNode(x);
newnode->next = pos;
pos->prev = newnode;
prev->next = newnode;
newnode->prev = prev;
}
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* next = pos->next;
LTNode* prev = pos->prev;
free(pos);
next->prev = prev;
prev->next = next;
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
free(cur);
cur = cur->next;
}
free(phead);
}
不同点 顺序表 链表
存储空间上 物理上一定连续
逻辑上连续,但物理上不一定
连续
随机访问 支持O(1) 不支持:O(N)
任意位置插入或者删除
元素
可能需要搬移元素,效率低
O(N)
只需修改指针指向
插入
动态顺序表,空间不够时需要
扩容
没有容量的概念
应用场景 元素高效存储+频繁访问 任意位置插入和删除频繁
缓存利用率 高 低