一. 单链表的定义、插入与删除
1.定义一个单链表
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
typedef <数据类型> <别名> 用别名来代替该数据类型名
LinkList为指向该单链表的指针。
2.初始化一个空的单链表,与判断单链表是否为空
1.不带头结点的单链表
bool InitList(LinkList &L)
{
L = NULL;
return true;
}
bool Empty(LinkList L)
{
return (L == NULL);
}
2.带头结点的单链表
bool InitList(LinkList &L)
{
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = NULL;
return true;
}
bool Empty(LinkList L)
{
return (L->next == NULL);
}
3.按位序插入
1.带头结点
bool ListInsert(LinkList &L, int i, ElemType e)
{
if (i < 1)
return false;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) //寻找插入位序的前一个节点
{
p = p->next;
j++;
}
if (p == NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
2.不带头结点
bool ListInsert(LinkList &L, int i, ElemType e)
{
if (i < 1)
return false;
if (i == 1)
{
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s; //头指针指向头节点
}
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) //寻找插入位序的前一个节点
{
p = p->next;
j++;
}
if (p == NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
4.指定节点的插入操作
1.后插
bool InsertNextNode(LNode *p, ElemType e)
{
if (p == NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
if (s == NULL)
return false; //内存分配失败
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
2.前插
bool InsertPriorNode(LNode *p, ElemType e)
{
if (p == NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
if (s == NULL)
return false; //内存分配失败
s->next = p->next;
p->next = s;
s->data = p->data;
p->data = e;
return true;
}
5.按位序删除(带头结点)
bool ListDelete(LinkList &L, int i, ElemType &e)
{
if (i < 1)
return false;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) //寻找插入位序的前一个节点
{
p = p->next;
j++;
}
if (p == NULL)
return false; //i值不合法
if (p->next == NULL)
return false; //第i-1个节点后已无其他节点
LNode *q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
6.指定节点的删除
bool DeleteNode(LNode *p)
{
if (p == NULL)
return false;
LNode *q = p->next;
p->data = p->next->data;
p->next = q->next;
free(q);
return true;
}
但如果p是最后一个节点,只能从表头以循环的方式寻找p的前驱节点。时间复杂度为O(n)
二. 单链表的查找
1.按位查找(带头结点)
LNode* GetElem(LinkList L, int i)
{
if (i < 0)
return NULL;
LNode *p;
int j = 0;
p = L; //L指向头结点
while (p != NULL && j < i)
{
p = p->next;
j++;
}
return p;
}
2.按值查找(带头结点)
LNode* LocateElem(LinkList L, ElemType e)
{
LNode *p = L->next; //从第一个节点开始查找
while (p != NULL && p->data != e)
p = p->next;
return p;
}
3.求表的长度(带头结点)
int Length(LinkList L)
{
int len = 0;
LNode *p = L;
while (p->next != NULL)
{
p = p->next;
len++;
}
return len;
}
三.单链表的建立
1.尾插法建立单链表
LinkList List_TailInsert(LinkList &L)
{
int x;
L = (LinkList)malloc(sizeof(LinkList));
L->next = NULL;
LNode *s, *r = L;
cin >> x;
while (x != 9999) //输入9999表示结束
{
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = r->next;
r->next = s;
r = s;
cin >> x;
}
r->next = NULL;
return L;
}
2.头插法建立单链表
LinkList List_HeadInsert(LinkList &L)
{
LNode *s;
int x;
L = (LinkList)malloc(sizeof(LinkList));
L->next = NULL;
cin >> x;
while (x != 9999)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
cin >> x;
}
return L;
}
四.双链表
1.双链表的初始化与判空
typedef struct DNode
{
ElemType data;
struct DNode *prior, *next;
}DNode, *DLinklist;
bool InitDLinkList(DLinklist &L)
{
L = (DNode*)malloc(sizeof(DNode));
if (L == NULL)
return false;
L->prior = NULL;
L->next = NULL;
return true;
}
bool Empty(DLinklist L)
{
if (L->next == NULL)
return true;
else
return false;
}
判空其实能一行写完
2.在指定结点处后插新结点
bool InsertNextDNode(DNode *p, DNode *s)
{
if (p == NULL || s == NULL) //非法参数
return false;
s->next = p->next;
if (p->next != NULL) //如果p结点有后继结点
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
3.删除指定结点的后继结点
bool DeleteNextDNode(DNode *p)
{
if (p == NULL)
return false;
DNode *q = p->next; //找到p的后继结点
if (q == NULL)
return false;
p->next = q->next;
if (q->next != NULL) //q结点不是最后一个结点
q->next->prior = p;
free(q);
return true;
}
4.销毁双链表
void DestoryList(DLinklist &L)
{
while (L->next != NULL)
DeleteNextDNode(L);
free(L);
L = NULL;
}
五.循环链表
1.循环单链表
(1)初始化
//初始化一个循环单链表
bool InitList(LinkList &L)
{
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = L;
return true;
}
(2)判断是否为空
//判断循环单链表是否为空
bool Empty(LinkList L)
{
if (L->next == L)
return true;
else
return false;
}
(3)判断p结点是否为尾结点
//判断结点p是否为循环单链表的尾结点
bool isTail(LinkList L, LNode *p)
{
if (p->next == L)
return true;
else
return false;
}
2.循环双链表
//初始化一个循环双链表
bool InitDLinkList(DLinklist &L)
{
L = (DNode*)malloc(sizeof(DNode));
if (L == NULL)
return false;
L->prior = L;
L->next = L;
return true;
}
这里就给出了其初始化操作,其余操作与双链表类似,而且很好写,就不列出了
六.相关题目
1.将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
建立一个指向指针的指针pp,两个链表的结点分别比较大小,小的先进入结果链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
while (list1 != nullptr && list2 != nullptr)
{
ListNode** pp = (list1->val < list2->val) ? &list1 : &list2;
cur->next = *pp;
cur = cur->next;
*pp = (*pp)->next;
}
cur->next = (list1 == nullptr) ? list2 : list1;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
2.给定一个已排序的链表的头 head
, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
因为是已排序的链表,只需比较相邻两个结点即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (head==NULL)
return head;
ListNode *s = head;
while (s->next != NULL)
{
if (s->val == s->next->val)
s->next = s->next->next;
else
s = s->next;
}
return head;
}
};
3.给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
Floyd 判圈算法,用快慢指针可以就地解决。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == NULL)
return nullptr;
ListNode *slowPtr = head, *fastPtr = head;
bool existPtr = false;
while (fastPtr->next != NULL && fastPtr->next->next != NULL)
{
slowPtr = slowPtr->next;
fastPtr = fastPtr->next->next;
if (slowPtr == fastPtr)
{
existPtr = true;
}
if (existPtr)
{
slowPtr = head;
while (slowPtr != fastPtr)
{
slowPtr = slowPtr->next;
fastPtr = fastPtr->next;
}
return slowPtr;
}
}
return nullptr;
}
};
4.试编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点唯一)。
用minp保存值最小的结点指针(初值为p)
LinkList Delete_Min(LinkList &L)
{
LNode *pre = L, *p = pre->next;
LNode *minpre = pre, *minp = pre;
while (p != NULL)
{
if (p->data < minp->data)
{
minp = p;
minpre = pre;
}
pre = p;
p = p->next;
}
minpre->next = minp->next;
free(minp);
return L;
}
5.有一个带头结点的单链表L,设计一个算法使其元素递增有序。
采用直接插入排序的思想,先构成一个只含一个数据结点的有序单链表,然后依次扫描单链表中剩下的结点*p,通过比较找到前驱节点*pre,插入到*pre之后。
void Sort(LinkList &L)
{
LNode *p = L->next, *pre;
LNode *r = p->next;
p->next = NULL;
p = r;
while (p != NULL)
{
r = p->next;
pre = L;
while (pre->next != NULL && pre->next->data < p->data)
pre = pre->next;
p->next = pre->next;
pre->next = p;
p = r;
}
}