1.从尾头到打印单链表
void PrintReverse(ListNode *first)
{
ListNode *end = NULL;
while (end != first)
{
ListNode *cur = first;
//找到要打印的结点
while (cur->next != end)
{
cur = cur->next;
}
printf("%d ", cur->data);
end = cur;
}
printf("\n");
}
用递归的方法求解:
a1 a2 a3 ........ an
递归:
- a1 结果已知
- a(k+1) 的结果可以通过 a(k) 结果推导出来
//递归
void PrintReverseRecursion(ListNode *first)
{
if (first->next == NULL){
printf("%d ", first->data);
}
else{
PrintReverseRecursion(first->next);
//链表中除了 first 之外的所有结点都逆序打印了
printf("%d ", first->data);
}
}
2.逆转/反转单链表
传入链表,进行逆置/反转,返回逆置/反转后的链表的第一个结点地址,原链表将无效
1-->2-->3-->4-->5
5-->4-->3-->2-->1
头删---->头插
ListNode *ReverseList(ListNode *first)
{
ListNode *cur = first;
ListNode *node;
ListNode *result = NULL;
while (cur != NULL)
{
//从原来链表中头删(光摘下来,没有真正删除)
node = cur;
cur = cur->next;
//node 就是被摘下来的结点
node->next = result;
result = node;
}
return result;
}
方法二:
不调整值的位置,直接改变链表指针指向的方向。即:
1-->2-->3-->4-->5-->NULL
NULL<--1<--2<--3<--4<--5
ListNode *ReverseList2(ListNode *first)
{
ListNode *p1 = NULL;
ListNode *p2 = first;
ListNode *p3 = first->next;
while (p2 != NULL)
{
p2->next = p1;
p1 = p2;
p2 = p3;
if (p3 != NULL)
{
p3 = p3->next;
}
}
return p1;
}
3.删除一个无头单链表的非尾节点(不能遍历链表)
void RemoveNoFirst(ListNode *pos)
{
pos->data = pos->next->data;
ListNode *del = pos->next;
pos->next = pos->next->next;
free(del);
}
4.在无头单链表的一个节点前插入一个节点(不能遍历链表)
void InsertNoFirst(ListNode *pos, DataType data)
{
ListNode *cur = pos;
cur->next = pos->next;
pos->next = cur;
pos->data = data;
}
5.约瑟夫环
ListNode *JosephCycle(ListNode *first, int k)
{
//第一步,链表构成环
ListNode *tail = first;
while (tail->next != NULL)
{
tail = tail->next;
}
//tail就是最后一个节点
tail->next = first;
//第二步
ListNode *cur = first;
//结束条件是链表中只有一个结点
while (cur->next == NULL){
ListNode *prev = NULL;
for (int i = 0; i < k - 1; i++){
prev = cur;
cur = cur->next;
}
//cur就是我们要删除的结点
prev->next = cur->next;
free(cur);
//让循环继续
cur = prev->next;
}
cur->next = NULL;
return cur;
}
6.合并两个有序链表,合并后依然有序(升序)
ListNode* MergeOrderedList(ListNode *list1, ListNode *list2)
{
assert(list1);
assert(list2);
ListNode *p1 = list1;
ListNode *p2 = list2;
ListNode *result = NULL;
while (p1 != NULL && p2 != NULL)
{
if (p1->data < p2->data){
ListPushBack(&result, p1->data);
p1 = p1->next;
}
else{
ListPushBack(&result, p2->data);
p2 = p2->next;
}
}
// 一个链表为空了
if (p1 == NULL) {
while (p2 != NULL){
ListPushBack(&result, p2->data);
p2 = p2->next;
}
}
if (p2 == NULL) {
while (p1 != NULL){
ListPushBack(&result, p1->data);
p1 = p1->next;
}
}
return result;
}
优化:
ListNode * MergeOrderedListAd(ListNode *list1, ListNode *list2)
{
ListNode *cur1 = list1;
ListNode *cur2 = list2;
ListNode *result = NULL; //结果链表
ListNode *tail = NULL; //结果链表的最后一个结点,方便尾插
ListNode *next; //保存遍历过程的下一个结点
ListNode *node;
while (cur1 != NULL && cur2 != NULL)
{
if (cur1->data <= cur2->data)
{
node = cur1;
}
else
{
node = cur2;
}
next = node->next;
if (result != NULL)
{
//链表不为空,则做尾插处理
tail->next = node;
}
else
{
//保存链表的下一个结点,让循环继续
result = node;
}
node->next = NULL;
//保存新的最后一个结点
tail = node;
if (node == cur1)
{
cur1 = next;
}
else
{
cur2 = next;
}
//一个链表空了
if (cur1 == NULL)
{
tail->next = cur2;
}
if (cur2 == NULL)
{
tail->next = cur1;
}
return result;
}
}
7.求两个已排序单链表中相同的数据。
void Unionset(ListNode *list1, ListNode *list2)
{
ListNode *cur1 = list1;
ListNode *cur2 = list2;
while (cur1 != NULL && cur2 != NULL)
{
if (cur1->data < cur2->data)
{
cur1 = cur1->next;
}
else if (cur1->data > cur2->data)
{
cur2 = cur2->next;
}
else
{
printf("%d ", cur1->data);
cur1 = cur1->next;
cur2 = cur2->next;
}
}
printf("\n");
}
另一种方法:
void UnionsetDup(ListNode *list1, ListNode *list2)
{
ListNode *cur1 = list1;
ListNode *cur2 = list2;
DataType data;
while (cur1 != NULL && cur2 != NULL)
{
if (cur1->data < cur2->data)
{
cur1 = cur1->next;
}
else if (cur1->data > cur2->data)
{
cur2 = cur2->next;
}
else
{
printf("%d ", cur1->data);
data = cur1->data;
while (cur1 != NULL && cur1->data == data)
{
cur1 = cur1->next;
}
while (cur2 != NULL && cur2->data == data)
{
cur2 = cur2->next;
}
}
}
printf("\n");
}
8.查询单链表的中间结点,要求只遍历一遍
void FindMid(ListNode *first)
{
ListNode *fast = first;
ListNode *slow = first;
while (1)
{
fast = fast->next;
if (fast == NULL)
{
break;
}
fast = fast->next;
if (fast == NULL)
{
break;
}
slow = slow->next;
}
printf("%d\n", slow->data);
}
9.查找单链表的倒数第k个结点,要求只遍历一次链表
void FindTailK(ListNode *first, int k)
{
ListNode *forward = first;
ListNode *backward = first;
while (k--)
{
forward = forward->next;
}
while (forward != NULL)
{
forward = forward->next;
backward = backward->next;
}
printf("%d\n", backward->data);
}
10.打印链表
void PrintResult(ListNode* result)
{
ListNode *cur;
for (cur = result; cur != NULL; cur = cur->next)
{
printf("%d->", cur->data);
}
printf("NULL\n");
}
11.测试函数和主函数:
//测试
void TestPrintReverse()
{
ListNode *first = NULL;
ListPushBack(&first, 1);
ListPushBack(&first, 2);
ListPushBack(&first, 3);
ListPushBack(&first, 4);
ListPushBack(&first, 5);
printf("倒序打印:");
PrintReverse(first);
printf("递归:");
PrintReverseRecursion(first);
printf("\n");
ListNode *result = ReverseList(first);
printf("逆置打印:");
PrintResult(result);
ListNode *sur = JosephCycle(first, 4);
printf("约瑟夫环:%d \n", sur->data);
ListNode *list1 = NULL;
ListPushBack(&list1, 1);
ListPushBack(&list1, 2);
ListPushBack(&list1, 3);
ListPushBack(&list1, 5);
ListPushBack(&list1, 7);
ListNode *list2 = NULL;
ListPushBack(&list2, 4);
ListPushBack(&list2, 6);
ListPushBack(&list2, 8);
ListPushBack(&list2, 9);
printf("合并链表:");
PrintResult(MergeOrderedList(list1, list2));
printf("找相同数据:");
ListNode *list3 = NULL, *list4 = NULL;
ListPushBack(&list3, 1);
ListPushBack(&list3, 3);
ListPushBack(&list3, 4);
ListPushBack(&list3, 5);
ListPushBack(&list4, 1);
ListPushBack(&list4, 2);
ListPushBack(&list4, 4);
Unionset(list3, list4);
UnionsetDup(list3, list4);
printf("找中间结点:");
FindMid(first);
printf("倒数第k个结点:");
FindTailK(list1, 3);
}
int main()
{
TestPrintReverse();
return 0;
}
12.附:链表实现的源文件"List.c"
注明:上面的函数实现调用了这里的函数
#include <stdlib.h>
#include <assert.h>
typedef int DataType;
typedef struct ListNode {
DataType data;
struct ListNode *next;
} ListNode;
// 初始化/销毁
void ListInit(ListNode **ppFirst)
{
assert(ppFirst != NULL);
*ppFirst = NULL;
}
void ListDestroy(ListNode **ppFirst)
{
// TODO:
*ppFirst = NULL;
}
// 增删查改
static ListNode * CreateNode(DataType data)
{
ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
assert(newNode);
newNode->data = data;
newNode->next = NULL;
return newNode;
}
//头插
void ListPushFront(ListNode **ppFirst, DataType data)
{
assert(ppFirst != NULL);
// 考虑特殊情况,链表为空 *ppFirst == NULL
// 正常情况
// 1. 指针 vs 指向空间;从堆上申请空间
ListNode *newNode = CreateNode(data);
newNode->next = *ppFirst;
*ppFirst = newNode;
}
//尾插
void ListPushBack(ListNode **ppFirst, DataType data)
{
ListNode *newNode = CreateNode(data);
// 特殊情况,找倒数第一个 -> 至少有一个,所以特殊情况是链表为空
if (*ppFirst == NULL) {
*ppFirst = newNode;
return;
}
// 通常情况
ListNode *cur = *ppFirst;
while (cur->next != NULL) {
cur = cur->next;
}
// cur 就是最后一个结点
cur->next = newNode;
}
// 删除
//头删
void ListPopFront(ListNode **ppFirst)
{
assert(ppFirst != NULL); // 变量地址不为 NULL
assert(*ppFirst != NULL); // 不能是空链表
ListNode *del = *ppFirst;
*ppFirst = del->next;
free(del); // 谨记
}
void ListPopBack(ListNode **ppFirst)
{
assert(ppFirst != NULL); // 变量地址不为 NULL
assert(*ppFirst != NULL); // 不能是空链表
// 链表中只有一个结点
if ((*ppFirst)->next == NULL) {
free(*ppFirst);
*ppFirst = NULL;
return;
}
// 正常情况
ListNode *del;
ListNode *cur = *ppFirst;
while (cur->next->next != NULL) {
cur = cur->next;
}
del = cur->next;
cur->next = NULL;
free(del);
}
// 查找
ListNode * ListFind(ListNode *first, DataType data)
{
// 顺序查找,去遍历
for (ListNode *cur = first; cur != NULL; cur = cur->next) {
if (data == cur->data) {
return cur;
}
}
return NULL;
}
// 在结点前做插入(结点 pos 肯定在链表中 && pos 不是空【链表不是空】)
void ListInsert(ListNode **ppFirst, ListNode *pos, DataType data)
{
// 头插
if (*ppFirst == pos) {
ListPushFront(ppFirst, data);
return; // 记得 return,否则下面加上 else
}
ListNode *cur = *ppFirst;
// 找 pos 的前一个结点
while (cur->next != pos) {
cur = cur->next;
}
// 插入新结点
ListNode *newNode = CreateNode(data); // 一定要申请空间
newNode->next = cur->next; // or pos;
cur->next = newNode;
}
// 删除指定结点(结点 pos 肯定在链表中 && pos 不是空【链表不是空】
void ListErase(ListNode **ppFirst, ListNode *pos)
{
// 头删
if (*ppFirst == pos) {
ListPopFront(ppFirst);
return; // 记得 return,否则下面加上 else
}
ListNode *cur = *ppFirst;
// 找 pos 的前一个结点
while (cur->next != pos) {
cur = cur->next;
}
cur->next = pos->next;
free(pos); // 记得
}
void Test()
{
ListNode *first;
ListInit(&first); // 传值 1. first 传地址 2. &first
// 链表是一条空链表, first == NULL
// first 会变化
ListPushBack(&first, 1);
// 以后 first 不变了
ListPushBack(&first, 2);
ListPushBack(&first, 3);
ListNode *result = ListFind(first, 2); // 传地址 or 传值?
ListInsert(&first, result, 0);
}