做试卷挣钱的网站广告联盟点击广告能赚多少
利用C语言实现链表,并定义一些常用的操作
文章目录
- 链表定义
- 新建一个链表结点
- 打印链表
- 插入结点
- 头插法(常用)
- 运行
- 尾插法(使用较少)
- 运行
- 返回链表长度
- 链表转置
- 运行
- 合并两个有序的链表
- 运行
- 删除最小结点
- 运行
- 打印倒数第k个元素
- 运行
- 循环右移k个位置
- 运行
- 判断链表是否有环
- 运行
链表定义
使用typedef简化代码
#include <stdio.h>
#include <stdlib.h>typedef struct LinkedList {int data;struct LinkedList *next;
} LinkedList;
新建一个链表结点
LinkedList *createLinkedList(int i) {LinkedList *list = (LinkedList *)malloc(sizeof(LinkedList));list->data = i;list->next = NULL;return list;
}
打印链表
void printAll(LinkedList *head) {LinkedList *current = head;while (current != NULL) {printf(" --->%d", current->data);current = current->next;}printf("\n");
}
插入结点
头插法(常用)
采用头插法产生的链表和输入的数字是逆序的。
例如:一次插入1,2,3,4,5。链表为:5–>4 -->3 -->2 -->1
LinkedList **p
是指向指针的指针,用于直接修改调用者作用域中的头指针。如果采用LinkedList *p
那么只能修改指针中的数值,而不能直接修改头指针,那么头插法也就没法实现了。
void insertAtHead(LinkedList **p, int data) {LinkedList *newNode = malloc(sizeof(LinkedList));newNode->data = data;newNode->next = *p;*p = newNode;
}
运行
int main() {LinkedList *head2 = NULL;insertAtHead(&head2, 1);insertAtHead(&head2, 2);insertAtHead(&head2, 3);insertAtHead(&head2, 4);printAll(head2);return 0;
}
尾插法(使用较少)
遍历到最后一个结点,然后把目标结点挂到链表上
void insertAtTail(LinkedList **p, int data) {LinkedList *newNode = malloc(sizeof(LinkedList));newNode->data = data;newNode->next = NULL;if (*p == NULL) {*p = newNode;}else {LinkedList *current = *p;// 遍历到最后一个节点while (current->next != NULL) {current = current->next;}current->next = newNode;}
}
运行
与头插法顺序恰恰相反
int main() {LinkedList *head2 = NULL;insertAtTail(&head2, 1);insertAtTail(&head2, 2);insertAtTail(&head2, 3);insertAtTail(&head2, 4);printAll(head2);return 0;
}
返回链表长度
int lengthOfNode(LinkedList *head) {int length = 0;LinkedList *current = head;while (current != NULL) {length++;current = current->next;}return length;
}
链表转置
采用三指针法,每次循环保存prev和next,实现局部转置,直到全部结点都实现转置。此时current为NULL,prev刚好指向最后一个元素,也就是转置后的第一个元素,使其为头节点即可。
void reverseList(LinkedList **head) {LinkedList *current = *head;LinkedList *prev = NULL;LinkedList *next = NULL;while (current != NULL) {next = current->next; // 保存nextcurrent->next = prev; // 实现局部转置prev = current;current = next;}*head = prev;
}
运行
int main() {LinkedList *head2 = NULL;insertAtHead(&head2, 1);insertAtHead(&head2, 3);insertAtHead(&head2, 5);insertAtHead(&head2, 6);insertAtHead(&head2, 99);printAll(head2);reverseList(&head2);printAll(head2);return 0;
}
合并两个有序的链表
创建一个临时链表,当两个链表都不为空时,比较链表结点大小,总是将值小的链表结点挂到临时链表上,当有一个链表为空时,直接将剩下的那个链表结点全部挂到临时链表即可。
LinkedList *mergeList(LinkedList *a, LinkedList *b) {LinkedList *pHead = malloc(sizeof(LinkedList));pHead->data = -1;LinkedList *current = pHead;while (a!=NULL && b!=NULL) {LinkedList *tmp = malloc(sizeof(LinkedList));if (a->data < b->data) {tmp->data = a->data;current->next = tmp;current = current->next;a = a->next;}else {tmp->data = b->data;current->next = tmp;current = current->next;b = b->next;}}if (a!=NULL) {current->next = a;}if (b!=NULL) {current->next = b;}return pHead->next;
}
运行
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 1);insertAtTail(&head1, 3);insertAtTail(&head1, 8);printAll(head1);LinkedList *head2 = NULL;insertAtTail(&head2, 2);insertAtTail(&head2, 7);insertAtTail(&head2, 99);printAll(head2);printAll(mergeList(head1, head2));return 0;
}
删除最小结点
先创建一个值为-1的哑结点,为了简化对链表为NULL时的处理。
通过循环找到最小结点,并且记录最小结点的前结点。前结点下一个结点指向最小结点的下一个结点即可(跨过最小结点)
void deleteMinNode(LinkedList **head) {LinkedList *tmpHead = createLinkedList(-1);tmpHead->next = *head;LinkedList *cur = tmpHead->next;LinkedList *prev = tmpHead;LinkedList *prevMin = tmpHead;LinkedList *minNode = cur;while (cur != NULL) {if (cur->data < minNode->data) {prevMin = prev;minNode = cur;}prev = cur;cur = cur->next;}prevMin->next = minNode->next;*head = tmpHead->next;
}
运行
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 1);insertAtTail(&head1, 3);insertAtTail(&head1, 8);insertAtTail(&head1, 99);printAll(head1);deleteMinNode(&head1);printAll(head1);return 0;
}
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 88);insertAtTail(&head1, 3);insertAtTail(&head1, 8);insertAtTail(&head1, 99);printAll(head1);deleteMinNode(&head1);printAll(head1);return 0;
}
打印倒数第k个元素
利用快慢指针法,快指针先走k步;然后循环,快指针走到尾,慢指针刚好指向倒数第k个结点
void printLastKNode(LinkedList *head, int k) {LinkedList *current = head;LinkedList *slow = head;LinkedList *fast = NULL;while (k-->0) {fast = current->next;current = current->next;}while (fast != NULL) {slow = slow->next;fast = fast->next;}printf("--->%d", slow->data);
}
运行
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 88);insertAtTail(&head1, 3);insertAtTail(&head1, 8);insertAtTail(&head1, 99);printAll(head1);printLastKNode(head1, 1);printf("\n");printLastKNode(head1, 2);printf("\n");printLastKNode(head1, 4);return 0;
}
循环右移k个位置
将循环右移k个位置,例如k=2, 123456变为:561234
先找到分割结点的前一个结点,此时将链表断为两段,即:1–2–3–4–NULL 和 5–6–NULL
然后将后半段next指向前半段即可。
void rightCycleKNode(LinkedList **head, int k) {int length = 1;LinkedList *backNode = *head;while (backNode->next != NULL) {length++;backNode = backNode->next;}// now back is point at tail: 5--6--NULLint preK = length - k - 1;LinkedList *front = *head;LinkedList *newHead = NULL;// 找到分割结点的前一个结点,此时也可以快慢指针,先让快指针先走k步,然后快指针指向尾结点时,慢指针刚好指向分割结点的前一个结点while (preK-- > 0) {front = front->next;}// after loop, now node is point at 4newHead = front->next; // 此时new: 5--6--NULLfront->next = NULL; // cut front line: 1--2--3--4--NULL(head此时和front一样,只是指向的结点不一样)backNode->next = *head; // 把1234接到6的后边,6--1--2*head = newHead;
}
运行
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 1);insertAtTail(&head1, 2);insertAtTail(&head1, 3);insertAtTail(&head1, 4);insertAtTail(&head1, 5);insertAtTail(&head1, 6);printAll(head1);rightCycleKNode(&head1, 2);printAll(head1);return 0;
}
判断链表是否有环
采用快慢指针法,快指针每次走两步,慢指针每次走一步,如果存在环,快指针最终会沿着环与慢指针相遇(类似于跑步中的套圈)
运行
int main() {LinkedList *head1 = NULL;insertAtTail(&head1, 1);insertAtTail(&head1, 2);insertAtTail(&head1, 3);hasCycle(head1);head1->next->next->next = head1;hasCycle(head1);return 0;
}