单链表的概念
链表和顺序表的区别:
链表在逻辑存储上是连续的,在物理存储上是不连续的。
单链表属于链表中的一种,每一个存储数据的节点除了存储数据本身之外,还需要存储其直接后继结点的地址。
例: 32位系统存储10个整形(int 4个字节)数据:
顺序表存储: 总共需要的堆区空间: 40个字节
单链表存储: 总共需要的堆区空间: 80个字节
单链表的实现
-
头结点
-
头指针 – 在实现代码中会使用二级指针
头结点单链表
1、结构声明
typedef int DataType;
typedef struct Node
{
// 数据类型
union // 联合体 当使用其中一个时 就不能使用另一个
{
DataType data; // 存储有效数据的
int length; // 头结点使用,来存储结点个数的
};
// 结点指针类型
struct Node *next;
}LinkList;
2、方法的声明
// 初始化
void InitLinkList(LinkList *head);
// 销毁
void DestroyLinkList(LinkList *head);
// 插入
bool InsertLinkList(LinkList *head, DataType value, int pos);
bool InsertLinkListHead(LinkList *head, DataType value);
bool InsertLinkListRear(LinkList *head, DataType value);
// bool InsertLinkListValueBefore();
// bool InsertLinkListValueAfter();
// 删除
bool DeleteLinkList(LinkList *head, int pos);
bool DeleteLinkListHead(LinkList *head);
bool DeleteLinkListRear(LinkList *head);
bool DeleteLinkListValue(LinkList *head, DataType value);
// 判空
bool EmptyLinkList(LinkList *head);
// 长度
int GetLength(LinkList *head);
// 显示
void ShowLinkList(LinkList *head);
3、方法的实现
#include "head_list.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
static LinkList *ApplyNode(DataType value, LinkList *point)
{
LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
if (new_node == NULL) return NULL;
new_node->data = value;
new_node->next = point;
return new_node;
}
// 初始化
void InitLinkList(LinkList *head)
{
if (head == NULL) exit(0);
head->length = 0;
head->next = NULL;
}
// 长度
int GetLength(LinkList *head)
{
if (head == NULL) exit(0);
return head->length;
}
// 插入
bool InsertLinkList(LinkList *head, DataType value, int pos)
{
if (head == NULL) exit(0);
if (pos < 0 || pos > GetLength(head)) return false;
// 找pos位置的前面一个结点
LinkList *p = head;
while (pos)
{
p = p->next;
pos--;
}
LinkList *new_node = ApplyNode(value, p->next);
if (new_node == NULL) return false;
p->next = new_node;
head->length++;
return true;
}
bool InsertLinkListHead(LinkList *head, DataType value)
{
if (head == NULL) exit(0);
LinkList *new_node = ApplyNode(value, head->next);
if (new_node == NULL) return false;
head->next = new_node;
head->length++;
return true;
}
bool InsertLinkListRear(LinkList *head, DataType value)
{
if (head == NULL) exit(0);
return InsertLinkList(head, value, GetLength(head));
}
// 删除
bool DeleteLinkList(LinkList *head, int pos)
{
if (head == NULL) exit(0);
if (pos < 0 || pos >= GetLength(head)) return false;
LinkList *p = head;
while (pos)
{
p = p->next;
pos--;
}
// 需要删除的节点是p的后继结点
LinkList *q = p->next;
p->next = q->next;
free(q);
head->length--;
return true;
}
bool DeleteLinkListHead(LinkList *head)
{
if (head == NULL);
return DeleteLinkList(head, 0);
}
bool DeleteLinkListRear(LinkList *head)
{
if (head == NULL) exit(0);
return DeleteLinkList(head, GetLength(head) - 1);
}
// 考虑有重复值的情况
bool DeleteLinkListValue(LinkList *head, DataType value)
{
if (head == NULL) exit(0);
LinkList *p = head;
LinkList *q = head->next;
while (q != NULL)
{
if (q->data == value)
{
p->next = q->next;
free(q);
q = p->next;
head->length--;
}
else
{
p = q;
q = q->next;
}
}
return true;
}
bool EmptyLinkList(LinkList *head)
{
if (head == NULL) exit(0);
return head->length == 0;
}
void ShowLinkList(LinkList *head)
{
if (head == NULL) exit(0);
LinkList *p = head->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// 销毁
void DestroyLinkList(LinkList *head)
{
if (head == NULL) exit(0);
while (!EmptyLinkList(head))
{
DeleteLinkListHead(head);
}
}
LinkList *FindOfK(LinkList *head, int k) // 0< k <= length
{
if (head == NULL) return NULL;
if (k <= 0 || k > head->length) return NULL;
int pos = head->length - k + 1;
LinkList *p = head;
while (pos)
{
p = p->next;
pos--;
}
return p;
}
// 不能直接获得length的情况
LinkList *FindOfK2(LinkList *head, int k)
{
if (head == NULL) return NULL;
if (k <= 0) return NULL;
LinkList *p = head;
while (k && p != NULL)
{
p = p->next;
k--;
}
if (p == NULL) return NULL;
LinkList *q = head;
while (p != NULL)
{
p = p->next;
q = q->next;
}
return q;
}
bool DeleteOfNode(LinkList *head, LinkList *p)
{
if (head == NULL || p == NULL) return false;
if (p->next == NULL) return DeleteLinkListRear(head);
LinkList *q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
return true;
}
void Reverse(LinkList *head)
{
if (head == NULL || head->next == NULL) return;
LinkList *p = NULL;
LinkList *q = head->next;
LinkList *s = q->next;
while (q != NULL)
{
q->next = p;
p = q;
q = s;
if (s != NULL) s = s->next;
}
head->next = p;
}
LinkList *IsIntersect(LinkList *head1, LinkList *head2)
{
if (head1 == NULL || head2 == NULL) return NULL;
LinkList *p = head1;
LinkList *q = head2;
if (head1->length > head2->length)
{
for (int i = 0; i < head1->length - head2->length; i++)
{
p = p->next;
}
}
else
{
for (int i = 0; i < head2->length - head1->length; i++)
{
q = q->next;
}
}
while (p != q)
{
p = p->next;
q = q->next;
}
return p;
}
LinkList *IsIntersect2(LinkList *head1, LinkList *head2)
{
if (head1 == NULL || head2 == NULL) return NULL;
LinkList *p = head1;
LinkList *q = head2;
while (p != q)
{
if (p != NULL) p = p->next;
else p = head2;
if (q != NULL) q = q->next;
else q = head1;
// p = p != NULL ? p->next : head2;
// q = q != NULL ? q->next : head1;
}
return p;
}
LinkList *IsLoop(LinkList *head)
{
if (head == NULL) return NULL;
LinkList *fast = head; // 快指针
LinkList *slow = head; // 慢指针
while (fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast) break;
}
if (fast == NULL || fast->next == NULL) return NULL;
LinkList *p = fast;
LinkList *q = head;
while (p != q)
{
p = p->next;
q = q->next;
}
return p;
}
头指针单链表
头指针和头结点实现的区别:
1、记录链表的类型不同,头指针是一个4字节的指针,记录第一个存储数据的节点的地址。头结点是一个结构体变量,使用这个变量的next域记录第一个存储数据的节点的地址,头结点中的data域可以使用联合体方式记录链表的长度。
2、各个操作方法的定义不同。如果是头指针实现,则方法中接受这个链表的类型就必须是二级指针。头结点实现的这些方式使用一级指针就OK。
1、结构的定义
typedef int DataType;
typedef struct Node
{
DataType data; // 记录本结点存储的数据
struct Node *next; // 记录下一个结点的地址
}LinkList;
2、方法的声明
// 初始化
void InitLinkList(LinkList **phead);
// 插入
bool InsertLinkList(LinkList **phead, DataType value, int pos);
bool InsertLinkListHead(LinkList **phead, DataType value);
bool InsertLinkListRear(LinkList **phead, DataType value);
// 删除
bool DeleteLinkList(LinkList **phead, int pos);
bool DeleteLinkListHead(LinkList **phead);
bool DeleteLinkListRear(LinkList **phead);
// 长度
int GetLength(LinkList **phead);
// 判空
bool Empty(LinkList **phead);
// 销毁
void DestroyLinkList(LinkList **phead);
// 显示
void ShowLinkList(LinkList **phead);
3、方法的实现
#include "list.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
static LinkList *ApplyNewNode(DataType value, LinkList *next)
{
LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
if (new_node == NULL) exit(0);
new_node->data = value;
new_node->next = next;
return new_node;
}
void InitLinkList(LinkList **phead) // phead中存储的值就是main方法中的phead变量的地址
{
if (phead == NULL) exit(0);
*phead = NULL;
}
int GetLength(LinkList **phead)
{
if (phead == NULL) exit(0);
int length = 0;
LinkList *p = *phead;
while (p != NULL)
{
length++;
p = p->next;
}
return length;
}
bool InsertLinkList(LinkList **phead, DataType value, int pos)
{
if (phead == NULL) exit(0);
if (pos < 0 || pos > GetLength(phead)) return false;
// 在头指针的后面插入新结点,头插法必须特殊处理: 因为只有在这需要修改头指针的值
if (pos == 0) return InsertLinkListHead(phead, value);// 头插
LinkList *p = *phead;
while (pos > 1)
{
p = p->next;
pos--;
}
// while循环结束后,就需要在p所指向的结点的后面插入新结点
/*
LinkList *new_node = ApplyNewNode(value, p->next);
p->next = new_node;
*/
p->next = ApplyNewNode(value, p->next);
return true;
}
bool InsertLinkListHead(LinkList **phead, DataType value)
{
if (phead == NULL) exit(0);
*phead = ApplyNewNode(value, *phead);
return true;
}
bool InsertLinkListRear(LinkList **phead, DataType value)
{
if (phead == NULL) exit(0);
// 链表本身是空的,尾插相当于头插
if (Empty(phead)) return InsertLinkListHead(phead, value);
LinkList *p = *phead;
while (p->next != NULL) p = p->next; // while循环结束后,p就是最后一个结点
p->next = ApplyNewNode(value, NULL);
return true;
}
bool Empty(LinkList **phead)
{
if (phead == NULL) exit(0);
if (*phead == NULL) return true;
return false;
}
void ShowLinkList(LinkList **phead)
{
if (phead == NULL) exit(0);
LinkList *p = *phead;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
bool DeleteLinkList(LinkList **phead, int pos)
{
if (phead == NULL) exit(0);
if (pos < 0 || pos >= GetLength(phead)) return false;
if (pos == 0) return DeleteLinkListHead(phead);
LinkList *p = *phead; // p指向的是第一个数据结点
while (pos > 1)
{
p = p->next;
pos--;
}
// while循环结束后,要删除的结点时p->next
LinkList *q = p->next;
p->next = q->next;
free(q);
return true;
}
bool DeleteLinkListHead(LinkList **phead)
{
if (phead == NULL) exit(0);
if (Empty(phead)) return false;
LinkList *p = *phead;
*phead = p->next;
free(p);
return true;
}
bool DeleteLinkListRear(LinkList **phead)
{
if (phead == NULL) exit(0);
if (Empty(phead)) return false;
LinkList *front = NULL;
LinkList *p = *phead;
while (p->next != NULL)
{
front = p;
p = p->next;
}
if (front == NULL) return DeleteLinkListHead(phead);
front->next = NULL;
free(p);
return true;
}
void DestroyLinkList(LinkList **phead)
{
if (phead == NULL) exit(0);
while (!Empty(phead))
{
DeleteLinkListHead(phead);
}
}
头指针单链表操作时的转化:
因为头结点实现的单链表相比于头指针实现的单链表,操作时特殊情况能少一些。在实现一些头指针的单链表的操作时,可以虚拟构建出一个头结点。
bool Funaction(LinkList **phead)
{
LinkList *head = (LinkList *)malloc(sizeof(LinkList)); // 虚拟出来的头结点
head->next = *phead;
LinkList *p = head; // p指向的头结点
LinkList *q = p->next;
while(q->next != NULL)
{
p = q;
q = q->next;
}
p->next = q->next;
free(q);
*phead = head->next;
free(head);
}