目录
-
- 单链表经典算法题目
-
- [1. 单链表相关经典算法OJ题:移除链表元素](#1. 单链表相关经典算法OJ题:移除链表元素)
- [2. 单链表相关经典算法QI题:链表的中间节点](#2. 单链表相关经典算法QI题:链表的中间节点)
- [3. 单链表相关经典算法QJ题:反转链表](#3. 单链表相关经典算法QJ题:反转链表)
- [4. 单链表相关经典算法QJ题:合并两个有序链表](#4. 单链表相关经典算法QJ题:合并两个有序链表)
- [5. 循环链表经典应用:环形链表的约瑟夫问题](#5. 循环链表经典应用:环形链表的约瑟夫问题)
- [6. 单链表相关经典算法 QJ题:分割链表](#6. 单链表相关经典算法 QJ题:分割链表)
单链表经典算法题目
1. 单链表相关经典算法OJ题:移除链表元素
题目:
思路一:
遍历原链表,遇到val就执行删除val节点的操作
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
//当链表的头结点的值为val时
while(head && head->val==val)
{
head = head->next;
}
//当链表为空的时候,直接返回
if (head == NULL) {
return head;
}
struct ListNode* pre = head;//定义临时指针指向与head一致
while (pre->next)
{
//当链表指向的下一个节点的值为val
if (pre->next->val == val) {
pre->next = pre->next->next;
} else {
/* 没找到,则继续遍历查找 */
pre = pre->next;
}
}
return head;
}
思路二:
定义新链表,遍历原链表找不为val的节点,尾插在新链表中
链表为空:插入进来的节点就是链表的头结点和尾结点
链表不为空:插入进来的节点就是新的尾结点
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* newHead = NULL;
struct ListNode* newtail = NULL;
struct ListNode* pcur = head;
while(pcur)
{
//当原链表pcur的val不为查找的val时,将此节点尾插到新节点中,否则就继续往下走
if(pcur->val != val)
{
//当链表为空
if(newHead == NULL)
{
newHead = newtail = pcur;
}else{
//链表不为空
newtail->next = pcur;
newtail = newtail->next;
}
}
pcur = pcur->next;
}
if(newtail)
{
newtail->next=NULL;
}
return newHead;
}
2. 单链表相关经典算法QI题:链表的中间节点
题目
思路一
使用循环遍历统计链表中的个数
使用for循环根据除以2结果走到中间节点
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* pre= head;
int flag = 0;//用于计数
while(pre)
{
flag++;//统计链表的个数
pre=pre->next;
}
int mid = flag/2;//获取中间的位置
pre=head;
for(int i=0;i<mid;i++)
{
pre=pre->next;
}
return pre;
}
思路二
根据快慢指针
先定义两个指针
slow指针每走一步
fast指针每走两步
当fast->next 为空,则slow刚好指向的就是中间节点
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
//定义两个指针
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast&&fast->next)
{
slow = slow->next;//一次走一步
fast = fast->next->next;//一次走两步。当fase走完的时候slow指向的就是中间的节点
}
return slow;
}
3. 单链表相关经典算法QJ题:反转链表
题目:
思路一
简单迭代
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
//定义两个指针
struct ListNode* temp = NULL;
struct ListNode* curr = NULL;
//循环遍历head
while(head){
temp = head->next;
head->next = curr;
curr = head;
head = temp;//head和temp已被事实上置空了,防止跑飞
}
return curr;
}
思路二
创建三个指针
分别记录前驱结点,当前节点,后继节点,改变原链表指针方向
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
//处理空链表
if(head==NULL)
{
return head;
}
//创建三个指针,分别记录前驱节点,当前节点以及后继节点
struct ListNode* n1 = NULL;
struct ListNode* n2 = head;
struct ListNode* n3 = head->next;
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3)
{
n3 = n3->next;
}
}
return n1;
}
4. 单链表相关经典算法QJ题:合并两个有序链表
题目
思路
在两个原链表中创建两个指针l1,l2
再创建一个新链表
l1和l2相比较,较小值的放入新链表中
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//当list1为空时直接返回list2
if(list1 == NULL)
{
return list2;
}
//同理
if(list2 == NULL)
{
return list1;
}
struct ListNode* l1 = list1;
struct ListNode* l2 = list2;
struct ListNode* newHead = NULL;
struct ListNode* newTail = NULL;
while(l1 && l2)
{
//当l1<l2
if(l1->val < l2->val)
{
//判读链表是否为空
if(newHead == NULL)
{
newHead = newTail = l1;
}else{
newTail->next = l1;
newTail = newTail->next;
}
l1 = l1->next;
}//l2<l1
else{
if(newHead == NULL)
{
newHead = newTail = l2;
}else{
newTail->next = l2;
newTail = newTail->next;
}
l2=l2->next;
}
}
//跳出循环时,出现两种情况l1为空,或者l2为空
if(l1)
{
newTail->next = l1;
}
if(l2)
{
newTail->next = l2;
}
return newHead;
}
但是我们可以看到,上述出现了重复的代码,如何优化解决呢?
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//当list1为空时直接返回list2
if(list1 == NULL)
{
return list2;
}
//同理
if(list2 == NULL)
{
return list1;
}
ListNode* l1 = list1;
ListNode* l2 = list2;
ListNode* newHead,*newTail;//申请一块哨兵位
newHead=newTail=(ListNode*)malloc(sizeof(ListNode));
while(l1 && l2)
{
//当l1<l2
if(l1->val < l2->val)
{
newTail->next = l1;
newTail = newTail->next;
l1 = l1->next;
}//l2<l1
else{
newTail->next = l2;
newTail = newTail->next;
l2=l2->next;
}
}
//跳出循环时,出现两种情况l1为空,或者l2为空
if(l1)
{
newTail->next = l1;
}
if(l2)
{
newTail->next = l2;
}
//malloc开辟了空间,但是这块空间用不了得释放掉
ListNode* ret = newHead->next;
free(newHead);
return ret;
}
5. 循环链表经典应用:环形链表的约瑟夫问题
著名的Josephus问题 据说著名犹太 Josephus有过以下的故事:
~
在罗⻢⼈占领乔塔帕特后,39 个犹太⼈与Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被⼈抓到,于是决定了⼀个⾃杀 ⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀ 个重新报数,直到所有⼈都⾃杀⾝亡为⽌。 历史学家
然⽽Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在 第16个与第31个位置,于是逃过了这场死亡游戏。
题目
思路
根据n来创建不带头单向链表
逢m删除当前节点
c
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @param m int整型
* @return int整型
*/
typedef struct ListNode ListNode;
ListNode* BuyNode(int x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->val = x;
newnode->next = NULL;
return newnode;
}
ListNode* createList(int n)
{
ListNode* phead = BuyNode(1);
ListNode* ptail = phead;
for(int i = 2;i<=n;i++)
{
ptail->next=BuyNode(i);
ptail = ptail->next;
}
//链表要首尾相连使其循环起来
ptail->next = phead;
return phead;
}
int ysf(int n, int m ) {
//根据n来创建不带头单向链表
ListNode* head = createList(n);
ListNode* pcur = head;
ListNode* prev = NULL;
int count = 1;
//逢m删除当前节点
while(pcur->next != pcur)
{
if(count == m)
{
//删除当前节点
prev->next = pcur->next;
free(pcur);
pcur = prev->next;
count = 1;
}else{
prev = pcur;
pcur = pcur->next;
count++;
}
}
//此时,节点便是唯一幸存下来的
return pcur->val;
}
6. 单链表相关经典算法 QJ题:分割链表
题目
思路
定义两个链表:大链表和小链表,遍历原来的节点将其放入对应的新链表中,最后将大链表和小链表的首尾相连
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
//小链表
ListNode* lessHead,*lessTail;
lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));//创建哨兵位
//大链表
ListNode* greaterHead,*greaterTail;
greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));//创建哨兵位
ListNode* pcur = head;
while(pcur)
{
if(pcur->val < x)
{
lessTail->next = pcur;
lessTail = lessTail->next;
}else{
greaterTail->next = pcur;
greaterTail = greaterTail->next;
}
pcur = pcur->next;
}
greaterTail->next = NULL;
lessTail->next = greaterHead->next;
ListNode* ret = lessHead->next;
free(lessHead);
free(greaterHead);
return ret;
}