目录
概念
线性表:指各数据元素间保持“1对1”关系的数据结构,分为顺序表和链表,分别通过元素相邻和保存指针域的方式实现“1对1”
顺序表:是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存 储。在数组上完成数据的增删查改。“随机存取”
链表:是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。“顺序存取”
ps.
1.设顺序表有n个数组元素,则读取第i个数组元素的平均时间复杂度为:O(1)
2.单链表的存储密度<1
表的增删查改
查找(定位)元素:按值查找
- 给定长度为n的线性表,查找值为v的元素
- (最坏)从头到尾遍历=>时间复杂度O(n)
顺序表新增/删除元素
- 给定长度为n的顺序表,在指定位置i插入一个新元素
- 给定长度为n的顺序表,删除位置i的元素
- 需要将位置i到位置n-1的所有元素都向后或向前挪一格
- 在最坏情况下(i=0)下,需要挪动全部的n个元素=>时间复杂度为O(n)
- 无需利用额外空间=>空间复杂度为O(1)
ps.
- 设顺序表中有n个元素,往其插入或从其删除一个元素,平均要移动的元素个数为 _____
答案:n/2 - 设顺序表中有n个元素,则时间复杂度为O(1)的操作是( )
A.访问第i个结点(i∈[1,n]) B.删除第i个结点(i∈[1,n])
C.在第i个结点后插入一个新元素(i∈[1,n]) D.将顺序表从小到大排序
答案:A
(单)链表新增元素
- 给定长度为 n 的单链表,在第i个结点插入一个新元素
- 首先需要从头结点开始逐个向后找i-1次=>时间复杂度为O(n)
- 找到后插入只需要修改第i-1个结点和待插入结点的[后继结点地址]即可=>O(1)
- 无需利用额外空间=>空间复杂度为O(1)
(单)链表删除元素
- 给定长度为 n 的单链表,删除第i个结点
- 需要移动到第i个结点的前驱结点,最坏情况下移动n-1次
- 修改前驱结点的后继指针=>O(1)
- 无需利用额外空间=>空间复杂度为O(1)
顺序表更新元素
- 给定长度为n的顺序表,更新位置i的元素
- 无论i的值如何,都可以通过i直接访问位置i元素,将其更新为v'=>时间复杂度为O(1)=>随机存取
(单)链表删除元素
- 给定长度为 n 的单链表,更新第i个结点的值
- 需要从头结点开始一个接一个找到第i个才能访问并更新它=>顺序存取
- 最坏情况遍历整个链表=>时间复杂度为O(n)
ps.
1.单链表的增删元素虽然不用移动元素,但需先找到其前驱结点,复杂度为O(n)
2.若线性表需要频繁更新元素 ->选择顺序表
3.若线性表需要频繁插入删除元素 ->选择链式表
(1)下列说法正确的是()
A.如果插入操作频繁发生在表头部,顺序表和单链表效率接近
B.如果插入操作频繁发生在表头部,顺序表比单链表效率更高
C.如果插入操作频繁发生在表尾部,顺序表比单链表效率更高
D.如果插入操作频繁发生在表尾部,顺序表和单链表效率接近
答案:C
线性表集合式合并:只合并不同元素
A:7,5,3,11
B:2,7,6,3
合并后:C:7,5,3,11,2,6
- 设A表长度为n,B表长度为m
- 对于B表中的每个元素,都需要先判断其是否已经存在A里=>O(mn)
- 如果存在,无需插入,如果不存在,将其插入在A的末尾=>O(1)
- 总时间复杂度为O(mn)
- 空间复杂度?顺序表:O(m+n)、链表:O(1)
合并两个有序表:本来分别有序,合并结果仍然有序
A:3,5,8,11
B:2,6,7,10
C:2,3,5,6,7,8,10,11
合并两个有序顺序表
- 设A表长度为n,B表长度为m
- 先预留结果表空间:n+m个元素
- 从表头开始同时逐个访问A表和B表元素,将当前位置上较小者放入结果表并后移一位
- 总时间复杂度为O(m+n)
- 空间复杂度为O(m+n)
合并两个有序链表
- 设A表长度为n,B表长度为m
- 先创建一个头结点(哑结点dummy),其数据没有实际意义,只为用它的[指针域]
- 从表头开始逐个同时遍历A和B,将当前已完成合并的表尾元素的后继节点设置为当前A和B游标中较小个,并将该游标向后移动一位
- 时间复杂度为O(m+n)、空间复杂度为O(1)
ps.
1.合并两个有序表:逐一比较两表当前元素,将正确的元素添加进结果表并移动游标
(1)合并两个长度为n的有序表,可能的最小比较次数为()
A.2n B.2n-1 C.n D.1
答案:C
分析:在合并两个有序表时,当一表游标移动至表尾时,直接将另一表剩下元素整体接在结果表后即可

顺序表代码实现:增删查改
//SeqList.h
typedef int SLDataType;
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct SeqList
{
SLDataType* a;//指向动态开辟数组
int size;//记录存储多少个有效数据
int capacity;//空间容量大小
}SL;
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps, int x);
void SLPopBack(SL* ps);
void SLPrint(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//中间插入删除
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//删除pos位置数据
void SLErase(SL* ps, int pos);
//int SLFind(SL* ps, SLDataType x);
//begin查找x的起始位置
int SLFind(SL* ps, SLDataType x,int begin);
//SeqList.c
#include"SeqList.h"
//顺序表初始化
void SeqListInit(SeqList* ps)
{
assert(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
//顺序表置空
void SeqListDestroy(SeqList* ps)
{
if (ps->a)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
}
//顺序表打印
void SeqListPrint(SeqList* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
SeqListInsert(ps, ps->size, x);
}
//顺序表头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
SeqListInsert(ps, 0, x);
}
//顺序表头删
void SeqListPopFront(SeqList* ps)
{
SeqListErase(ps, 0);
}
//顺序表尾删
void SeqListPopBack(SeqList* ps)
{
SeqListErase(ps, ps->size-1);
}
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* tmp = (SLDateType*)realloc(ps->a, newCapacity * sizeof(SLDateType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos的值
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
//test.c
#include "SeqList.h"
int main()
{
SeqList s;
SeqListInit(&s);
printf("尾插:1,2,3,4,5\n");
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPrint(&s);
printf("尾删5\n");
SeqListPopBack(&s);
SeqListPrint(&s);
printf("头插1\n");
SeqListPushFront(&s, 1);
SeqListPrint(&s);
printf("头删1\n");
SeqListPopFront(&s, 1);
SeqListPrint(&s);
printf("查找2并删除\n");
int pos = SeqListFind(&s, 2, 0);
if (pos != -1)
{
SeqListErase(&s, pos);
}
SeqListPrint(&s);
return 0;
}
单链表代码实现:增删查改
//SList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
//ps.需要修改头指针用二级指针,不需要修改用一级指针,只读不用指针
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
//链表为空不可以删
void SListPopBack(SListNode** pplist);
// 单链表头删
//链表为空不可以删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** plist);
//创建一个链表
SListNode* CreateSList(int n);
//SList.c
#include"SList.h"
//总结:
// 1.改变int,传递int*给形参,*形参进行交换改变
// 2.改变int*,传递int**给形参,*形参进行交换改变
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
SListNode* CreateSList(int n)
{
SListNode* phead = NULL, * ptail = NULL;
for (int i = 0; i < n; ++i)
{
SListNode* newnode = BuySListNode(i);
if (phead == NULL)
{
phead = ptail = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
return phead;
}
// 单链表打印
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur != NULL)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail = *pplist;
//找尾
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
/*SListNode* prev = NULL;
SListNode* tail = pplist;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
prev->next = NULL;*/
SListNode* tail = *pplist;
while (tail->next->next)
{
tail = tail->next;
}
tail->next = NULL;
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
SListNode* next = (*pplist)->next;
free(*pplist);
*pplist = next;
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SListNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SListNode* nextNode = pos->next;
pos->next = nextNode->next;
free(nextNode);
}
}
void SListDestroy(SListNode** pplist)
{
SListNode* cur = *pplist;
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pplist = NULL;
}
//test.c
#include"SList.h"
void TestList1()
{
SListNode* plist = CreateSList(10);
SListPrint(plist);
SListNode* pos = SListFind(plist, 5);
if (pos)
{
printf("找到了\n");
}
else
{
printf("找不到\n");
}
SListNode* p = SListFind(plist, 2);
SListInsertAfter(p, 20);
SListPrint(plist);
}
void TestList2()
{
SListNode* plist = NULL;
SListPushBack(&plist, 100);
SListPushBack(&plist, 200);
SListPushBack(&plist, 300);
SListPopBack(&plist);
SListPrint(plist);
SListPushFront(&plist, 1);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);
}
int main()
{
TestList1();
return 0;
}
带头双向循环链表

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都 是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了
头插:

尾插:

尾删:

增删查改代码实现:
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
LTNode* ListInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead, x);
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTErase(phead);
}
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead->next, x);
}
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void LTPopFront(LTNode* phead)
{
LTErase(phead->next);
}
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 = BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
free(pos);
prev->next = next;
next->prev = prev;
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
size_t LTSize(LTNode* phead)
{
assert(phead);
size_t size = 0;
LTNode* cur = phead->next;
while (cur != phead)
{
++size;
cur = cur->next;
}
return size;
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = cur->next;
}
free(phead);
phead = NULL;
}
637






