1.线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列.线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就是连续的一条直线.但是在物理结构上并不一定是连续,线性表在物理上存储时,通常以数组和链式结构的形式存储.
2.顺序表
2.1 概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改
2.2 实现
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;//指向动态数组指针
int size;//数据个数
int capacity;//容量
}SL;
//初始化
void SLInit(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPrint(SL* ps);
//尾插/尾删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
//头插/头删
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//任意位置插入删除
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
//查找和修改
int SLFind(SL* ps, SLDataType x);
void SLModify(SL* ps, int pos, SLDataType x);
SeqList.c
#include "SeqList.h"
void SLInit(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
assert(ps);
//检查容量空间,满了扩容
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : (ps->capacity * 2);
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newCapacity);
if (tmp == NULL)
{
perror("SLCheckCapacity::SL* tmp:");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity = newCapacity;
}
}
return;
}
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0;i < ps->size;i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SLPushBack(SL* ps, SLDataType x)
{
//assert(ps);
//SLCheckCapacity(ps);
//ps->a[ps->size] = x;
//ps->size++;
SLInsert(ps, ps->size,x);
}
void SLPopBack(SL* ps)
{
//assert(ps);
//assert(ps->size > 0);
//ps->size--;
SLErase(ps, ps->size -1);
}
void SLPushFront(SL* ps, SLDataType x)
{
//assert(ps);
//SLCheckCapacity(ps);
//for (int end = ps->size;end > 0;end--)
//{
// ps->a[end] = ps->a[end - 1];
//}
//ps->a[0] = x;
//ps->size++;
SLInsert(ps, 0, x);
}
void SLPopFront(SL* ps)
{
//assert(ps->size > 0);
//assert(ps);
//for (int start = 0;start < ps->size - 1;start++)
//{
// ps->a[start] = ps->a[start + 1];
//}
//ps->size--;
SLErase(ps, 0);
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int end = ps->size;end > pos;end--)
{
ps->a[end] = ps->a[end - 1];
}
ps->a[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int start = pos;start < ps->size-1;start++)
{
ps->a[start] = ps->a[start + 1];
}
ps->size--;
}
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0;i < ps->size;ps++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
void SLModify(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
3.链表
3.1 链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
3.2 链表的分类
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
1.单向或者双向
2.带头或者不带头
3.循环或者非循环
我们实际中最常用还是两种结构:
3.3 链表的实现
3.3.1 无头+单向+非循环链表增删查改实现
Slist.h
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
SLTNode* BuySListNode(SLTNode x);
//单链表的打印
void SListPrint(SLTNode* plist);
//单链表的尾插
void SListPushBack(SLTNode** plist, SLTDataType x);
//单链表头插
void SListPushFront(SLTNode** plist, SLTDataType x);
//单链表尾删
void SListPopBack(SLTNode** plist);
//单链表头删
void SListPopFront(SLTNode** plist);
//单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
void SListInsertAfter(SLTNode* pos, SLTDataType x);
void SListEraseAfter(SLTNode* pos);
SList.c
#include"SList.h"
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SLTNode* plist)
{
while (plist != NULL)
{
printf("%d->", plist->data);
plist = plist->next;
}
printf("NULL\n");
}
void SListPushBack(SLTNode ** plist, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
SLTNode* ptail = *plist;
if (*plist == NULL)
{
*plist = newnode;
}
else
{
while ((ptail)->next)
{
(ptail) = (ptail)->next;
}
(ptail)->next = newnode;
}
}
void SListPushFront(SLTNode** plist, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *plist;
*plist = newnode;
}
void SListPopBack(SLTNode** plist)
{
assert(*plist);
SLTNode* ptail = *plist;
//只有一个结点
if ((*plist)->next == NULL)
{
free(*plist);
*plist = NULL;
return;
}
//多个结点
while (ptail->next->next)
{
ptail = ptail->next;
}
free(ptail->next);
ptail->next = NULL;
}
void SListPopFront(SLTNode** plist)
{
assert(*plist);
SLTNode* phead = *plist;
*plist = (phead)->next;
free(phead);
phead = NULL;
}
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
return;
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
}
3.3.2 带头+双向+循环链表增删查改实现
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
LTNode* BuyListNode(LTDataType x);
LTNode* ListInit();
void ListPrint(LTNode* phead);
void ListPushBack(LTNode* phead, LTDataType x);
void ListPushFront(LTNode* phead, LTDataType x);
void ListPopBack(LTNode* phead);
void ListPopFront(LTNode* phead);
// 在pos位置之前插入x
void ListInsert(LTNode* pos, LTDataType x);
// 删除pos位置的节点
void ListErase(LTNode* pos);
List.c
#include"List.h"
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail:");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
LTNode* ListInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void ListPrint(LTNode* phead)
{
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
//LTNode* newnode = BuyListNode(x);
//LTNode* tail = phead->prev;
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
ListInsert(phead, x);
}
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
//LTNode* newnode = BuyListNode(x);
//LTNode* next = phead->next;
//next->prev = newnode;
//newnode->next = next;
//phead->next = newnode;
//newnode->prev = phead;
ListInit(phead->next, x);
}
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
//LTNode* del = phead->prev;
//LTNode* pre = del->prev;
//pre->next = phead;
//phead->prev = pre;
//free(del);
ListErase(phead->prev);
}
void ListPopFront(LTNode* phead)
{
//assert(phead);
//assert(phead->next != phead);
//LTNode* del = phead->next;
//LTNode* next = del->next;
//free(del);
//phead->next = next;
//next->prev = phead;
ListErase(phead->next);
}
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* pre = pos->prev;
pre->next = newnode;
newnode->prev = pre;
newnode->next = pos;
pos->prev = newnode;
}
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* pre = pos->prev;
LTNode* next = pos->next;
pre->next = next;
next->prev = pre;
free(pos);
}
4.顺序表和链表的优缺点
优点 | 缺点 | |
顺序表 | 1.支持随机访问 2.cpu高速缓存命中率高 | 头部或者中部插入效率低, 扩容(有一定程度性能消耗 ,可能存在一定程度空间浪费) |
链表(带头+双向+循环链表) | 1.任意位置插入删除O(1) 2.按需申请释放
| 不支持下标随机访问 |