链表的定义
链表的实现
通用头文件 fatal.h
#include <stdio.h>
#include <stdlib.h>
#define Error(Str) FatalError(Str)
#define FatalError(Str) fprintf(stderr, "%s\n", Str), exit(1)
链表结构及方法声明头文件 list.h
typedef int ElementType;
#ifndef _List_H
#define _List_H
struct Node;
typedef struct Node *PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;
List MakeEmpty(List L);
int IsEmpty(List L);
int IsLast(Position X, List L);
Position Find(ElementType X, List L);
void Delete(ElementType X, List L);
Position FindPrevious(ElementType X, List L);
void Insert(ElementType X, List L, Position P);
void DeleteList(List L);
Position Header(List L);
Position First(List L);
Position Advance(Position P);
ElementType Retrieve(Position P);
#endif
链表结构的实现 list.c
#include "list.h"
#include <stdlib.h>
#include "fatal.h"
struct Node
{
ElementType Element; // 节点数据
Position Next; // 指向下一个链表节点的指针
};
// 构造一个新的空链表
List
MakeEmpty(List L)
{
if (L != NULL)
DeleteList(L);
L = malloc(sizeof(struct Node));
if (L == NULL)
FatalError("Out of memory!");
L->Next = NULL;
return L;
}
// 测试是否是一个空链表
int
IsEmpty(List L)
{
return L->Next == NULL;
}
// 测试是否非空链表的最后一个有效元素
int
IsLast(Position P, List L)
{
return P->Next == NULL;
}
// 从前往后遍历链表找到元素数据等于目标数据的元素 返回指向这个节点的指针
Position
Find(ElementType X, List L)
{
Position P;
P = L->Next;
while (P != NULL && P->Element != X)
P = P->Next;
return P;
}
// 删除元素数据等于目标数据的节点
void
Delete(ElementType X, List L)
{
Position P, TmpCell;
P = FindPrevious(X, L);
if (!IsLast(P, L))
{
TmpCell = P->Next;
P->Next = TmpCell->Next;
free(TmpCell);
}
}
// 找到目标节点的前一个节点
Position
FindPrevious(ElementType X, List L)
{
Position P;
P = L;
while (P->Next != NULL && P->Next->Element != X)
P = P->Next;
return P;
}
// 在指定位置的后面插入新元素
void
Insert(ElementType X, List L, Position P)
{
Position TmpCell;
TmpCell = malloc(sizeof(struct Node));
if (TmpCell == NULL)
FatalError("Out of space!!!");
TmpCell->Element = X;
TmpCell->Next = P->Next;
P->Next = TmpCell;
}
// 删除整个链表 逐一释放每个链表节点
void
DeleteList(List L)
{
Position P, Tmp;
P = L->Next;
L->Next = NULL;
while (P != NULL)
{
Tmp = P->Next;
free(P);
P = Tmp;
}
}
// 返回链表的头部哨兵节点
Position
Header(List L)
{
return L;
}
// 返回链表的第一个实际节点
Position
First(List L)
{
return L->Next;
}
// 得到当前节点的下一个节点
Position
Advance(Position P)
{
return P->Next;
}
// 得到节点P的数据项
ElementType
Retrieve(Position P)
{
return P->Element;
}
测试代码 testlist.c
#include <stdio.h>
#include "list.h"
void
PrintList(const List L)
{
Position P = Header(L);
if (IsEmpty(L))
printf("Empty List\n");
else
{
do
{
P = Advance(P);
printf("%d ", Retrieve(P));
} while (!IsLast(P, L));
printf("\n");
}
}
main()
{
List L;
Position P;
int i;
L = MakeEmpty(NULL);
P = Header(L);
PrintList(L);
for (i = 0; i < 10; i++)
{
Insert(i, L, P);
PrintList(L);
P = Advance(P);
}
for (i = 0; i < 10; i += 2)
Delete(i, L);
for (i = 0; i < 10; i++)
// i为偶数的时候找不到 条件判断为 1 == 0 i为奇数的时候 找得到 条件判断为 0 == 1 所以代码逻辑没问题的情况下 下面的条件语句不会输出打印信息
if ((i % 2 == 0) == (Find(i, L) != NULL))
printf("Find fails\n");
printf("Finished deletetions\n");
PrintList(L);
DeleteList(L);
return 0;
}
测试输出如下:
Empty List
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8 9
Finished deletetions
1 3 5 7 9
链表实战
我们通过leetcode上面跟链表有关的1个算法题来练习下如何使用链表
- 第23题 给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ]
将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
题解如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
// Node 链表节点
typedef struct ListNode Node;
// 指向Node节点的指针
typedef Node *Position;
// 合并2个有序的链表
Position
_merge_two_list(Node *Left, Node *Right)
{
if (!Left || !Right)
return Left ? Left : Right;
// Header: 一个链表节点 它的next指向我们合并后的链表首节点
Node Header, *PreHeader = &Header;
// 用2个指针去遍历2个链表 谁被连接到新链表 谁就向前走一步
Position CurLeft = Left;
Position CurRight = Right;
while(CurLeft && CurRight)
{
if (CurLeft->val <= CurRight->val)
{
PreHeader->next = CurLeft;
CurLeft = CurLeft->next;
}
else
{
PreHeader->next = CurRight;
CurRight = CurRight->next;
}
// 新链表移动一步
PreHeader = PreHeader->next;
}
// 如果较短的链表刚好最后一轮被接入新链表 导致此时新链表尾部节点next为null 需要拼起来
PreHeader->next = CurLeft ? CurLeft : CurRight;
return Header.next;
}
Position
merge(struct ListNode **lists, unsigned int start, unsigned int end)
{
// end不是有效索引 所以start == end 返回空指针
if (start == end)
return NULL;
unsigned int mid = (start + end) / 2;
if (start == mid)
return lists[start];
// 归并排序
Position Left = merge(lists, start, mid);
Position Right = merge(lists, mid, end);
Position Res = _merge_two_list(Left, Right);
return Res;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){
return merge(lists, 0, listsSize);
}
代码贴到leetcode答题区即可
参考资料:
- 《数据结构与算法分析:C语言描述》