王道-考研-数据结构-顺序表和链表
不考研嘻嘻····插本随便复习一下
--顺序表--
按位查找
int GetElem(SqList L, int i)
{
// i 的值必须是合法的位序
if (i < 1 || i > L.length)
{
return false;
}
return L.data[i - 1];
}
- 时间复杂度:\(O(1)\)
. 按值查找
int LocateElem(SqList L, int e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0;
}
- 最好时间复杂度:\(O(1)\)
- 最坏时间复杂度:\(O(n)\)
- 平均时间复杂度:\(O(n)\)
--链表--
下面是按位插入,最基础最基本的一个插入了,需要掌握!!!
那个循环其实很简单,就是 $0 < i - 1 $ 意思就是把p节点移到我要插入的(i)的前一个元素,那么此时这个元素的next就是指向第i个节点了,然后进行替换,就这个流程。
注意这里的头结点是第0个元素,所以不存在要插入的 \(i <= 0\)
单链表的插入操作虽然不需要像顺序表的插入操作那样需要移动元素,但平均时间复杂度仍
为O(n)。这是因为 ,为了在第l个结点之前插入一个新结点,必须首先找到第i- 1 个结点, 其时
间复杂度为
- 最好时间复杂度:\(O(1)\)
- 最坏时间复杂度:\(O(n)\)
- 平均时间复杂度:\(O(n)\)
不带头结点的“按位插入”,\(j = 1\)
指定节点的后插节点【1】
时间复杂度:\(O(1)\)
指定节点的后插节点【2】
·删除操作
按位序删除【带头结点】
ListDelete(&L,i,&e)
步骤:
- 找到第 \(i-1\) 个结点
p
和被删除结点q
- 修改
e
的值为q
的数据元素值 - 修改
p
的后继结点为q
的后继结点 - 释放
q
// 按位序删除结点
bool ListDelete(LinkList &l, int i, int &e)
{
// i 的值必须是合法的位序
if (i < 1)
{
return false;
}
LNode *p;
int j = 0; // 当前 p 指向的是第几个结点
p = L; // 指向第 0 个结点
while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
{
p = p->next;
j++;
}
if (p == NULL) // i 值不合法
{
return false;
}
if (p->next == NULL) // 第 i-1 个结点后已无其他结点
{
return false;
}
LNode *q = p->next;
e = q->data; //其实这里就是,引用返回被删除的节点内容。
p->next = q->next;
free(q);
return true;
}
- 最好时间复杂度:\(O(1)\) 删除的是第一个节点
- 最坏时间复杂度:\(O(n)\)
- 平均时间复杂度:\(O(n)\)
指定结点删除
类似于前插操作,DeleteNode(LNode *p)
的步骤:
- 声明一个指针
q
指向结点p
的后继结点 - 修改
p
的数据元素为q
的数据元素 - 修改
p
的后继结点为q
的后继结点 - 释放
q
但是如果结点 p 是表尾结点,此方法将不适用,因为找不到 p 的后继结点。只能从表头开始查找并删除。
时间复杂度:\(O(1)\)
但是这代码存在BUG:不能删除最后的节点。因为最后的节点是null,null那来的值替换
???
单链表的局限性
无法逆向检索,有时候不太方便。
11. 按位查找(带头结点)
// 按位查找:返回第 i 个元素,带头结点,头结点看作是第 0 个结点
LNode *GetElem(LinkList L, int i)
{
if (i < 0)
{
return NULL;
}
LNode *p;
int j = 0; // 当前 p 指向的是第几个结点
p = L; // 指向第 0 个结点
while (p != NULL && j < i) // 循环找到第 i-1 个结点
{
p = p->next;
j++;
}
return p;
}
- 时间复杂度:\(O(n)\)
12. 按值查找(带头结点)
// 按值查找,找到数据域 == e 的结点
int LocateElem(LinkList L, int e)
{
LNode *p = L->next;
// 从第 1 个结点开始查找数据域为 e 的结点
while (p != NULL && p->data != e)
{
p = p->next;
}
// 找到后返回该结点指针,否则返回 NULL
return p;
}
- 时间复杂度:\(O(n)\)
13. 求表的长度
// 求表的长度
int Length(LinkList L)
{
int len = 0;
LNode *p = L;
while (p->next != NULL)
{
p = p->next;
len++;
}
return len;
}
- 时间复杂度:\(O(n)\)
14. 单链表的建立
单链表的建立步骤:
- 初始化一个单链表
- 每次取一个数据元素,插入到表尾/表头
14.1. 尾插法
思路一:
- 初始化单链表
- 设置变量
length
记录链表长度 while
循环:- 每次取一个数据元素
e
ListInsert(L, length+1, e)
插到尾部length++
- 每次取一个数据元素
- 时间复杂度:\(O(n^2)\)
思路二:
- 初始化单链表
- 设置指针
r
指向表尾元素 while
循环:- 在
r
之后插入结点s
- 修改
r
指向s
- 在
// 尾插法建立单链表
LinkList List_TailInsert(LinkList &L)
{
int x;
L = (LNode *)malloc(sizeof(LNode));
LNode *s, *r = L; // r 为表尾指针
scanf("%d", &x);
while (x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s; // r 指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL;
return L;
}
- 时间复杂度:\(O(n)\)
14.2. 头插法
- 初始化单链表
while
循环:- 每次取一个数据元素
e
InsertNextNode(L, e)
- 每次取一个数据元素
// 头插法建立单链表
LinkList List_HeadInsert(LinkList &L)
{
int x;
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL; // 一定要初始化头结点的 next
LNode *s;
scanf("%d", &x);
while (x != 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d", &x);
}
return L;
}
- 时间复杂度:\(O(n)\)
养成好习惯:只要是初始化单链表,就先把头指针指向 NULL。
头插法的重要应用:链表的逆置。
下面这个代码我也没看懂
LinkList List_Reverse(LinkList &L)
{
LinkList L2 = (LNode *)malloc(sizeof(LNode));
L2->next = NULL;
LNode *s = L->next;
while (s != NULL)
{
LNode *s2 = (LNode *)malloc(sizeof(LNode));
s2->data = s->data;
s2->next = L2->next;
L2->next = s2;
s = s->next;
}
return L2;
}
本文来自博客园,作者:咸瑜,转载请注明原文链接:https://www.cnblogs.com/bi-hu/p/16657051.html