有关单链表的面试题分析及代码实现
来源:互联网 发布:国内微电网数据统计 编辑:程序博客网 时间:2024/05/20 08:23
看下面的程序前,先创建一个单链表(仅供参考):
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node* next;
}Node, *pNode, *pList;
1.删除无头单链表的非尾结点
void EraseNotTail(pNode pos);
在单链表中,一般情况下要删除一个节点,我们会找到当前节点的前一个节点和后一个节点,需要把前一个节点的next指向后一个节点的地址,才可以删除当前节点;但是这里给的是一个无头的链表,函数只传进来一个要删除的节点,这该怎么删除呢?
我们要换一种删法:
看下图,现在指定要删除data1节点,可以通过把data2的值赋给data1,这样就把data1的值覆盖了,把data1的next指向data3,然后把data2节点删除掉
看代码:
pos是要删除的节点的地址
//删除无头单链表的非尾结点void EraseNotTail(pNode pos){pNode del = pos->next;pos->data = pos->next->data;pos->next = pos->next->next;free(del);}
2.逆置/翻转单链表
void ReverseList(pList* pplist);
单链表是只能往前走,不能往回走,那么怎样逆置一个单链表呢?
我们可以新建一个链表newplist;然后遍历旧链表*pplist,每找到一个节点,就把这个节点头插到新链表上(注意是在新表头插入),直到把旧链表遍历完,把所有节点都头插到新链表上,这时候新链表已经是旧链表的逆序了,然后在把旧的表头地址赋给新链表的表头,这是旧的链表也是逆序的了
看逆置单链表的代码:
void ReverseList(pList* pplist) //*pplist是链表的头指针{assert(pplist);pNode tmp = NULL;pList newlist = NULL; //这里创建一个新链表,来放翻转的pNode cur = *pplist;if ((*pplist == NULL) || ((*pplist)->next == NULL)){return;}while (cur != NULL) {tmp = cur;cur = cur->next; //这里不能写在下面,若写在下面tmp->next = NULL; 就把cur的next置成空了tmp->next = newlist;newlist = tmp;}*pplist = newlist;return;}
3.在无头单链表的一个非头结点前插入一个节点
void InsertFrontNode(pNode pos,DataType d);
什么意思呢?就是给你一个节点的位置,这个节点不是头结点,然后在这个节点的前面插一个数据为d的节点。
怎么办呢?
这里也没有告诉链表的头指针,所以我们无法知道当前节点的前一个节点,所以无法用传统的方法插入;
这里用到思想是插到当前位置的后面,然后在交换值
把要插入的节点先插入到当前节点的后面,然后在交换两个节点的data,这样就等于插在了当前节点的前面,看字不如看下图
代码如下:
void InsertFrontNode(pNode pos, DataType d) //插到当前位置的后面,然后在交换值{pNode newNode = (pNode)malloc(sizeof(Node));newNode->data = d;newNode->next = NULL;newNode->next = pos->next;pos->next = newNode;DataType tmp = pos->data;pos->data = newNode->data;newNode->data = tmp;return;}
4.查找链表中间节点
pNode FindMidNode(pList plist);
直接说思想,这里用的是快慢指针的思想,遍历链表时,一个fast指针每次走两个节点,一个slow每次走一个节点,它们是同时从表头出发的,所以当fast指针走到链表 的尾部时,这个slow慢指针肯定才走到链表中间,这样就找到了链表的中间节点。(这里不考虑链表的节点个数是偶数还是奇数,如果是偶数个节点,最中间两个节点的任意一个都算中间节点 )
看代码:
pNode FindMidNode(pList plist){pNode fast = plist;pNode slow = plist;while (fast != NULL&&fast->next != NULL){fast = fast->next->next;slow = slow->next;}return slow;}
5.删除单链表的倒数第K个节点(k>1&&k<链表长度)时间复杂度为O(N);
void DelKNode(pList* pplist, int k);
什么意思呢?
删除链表的倒数第k个节点,k=1等于是尾删,k=链表长度等于头删,所以规定(k>1&&k<链表长度),且只能遍历一次链表
怎么办呢?
这里也用的是快慢指针的思想;先让快指针走k个节点,然后慢指针再开始走,当快指针走到链表的尾部时,慢指针就走到了倒数第K个节点
下面来看代码理解:
void DelKNode(pList* pplist, int k) //*pplist是表头指针,k是要删除的倒数第几个数{assert(pplist);if (k <= 1 || k >= GetLinkListLen(*pplist)) //GetLinkListLen()是计算链表的长度的函数,下面有代码{printf("输入有误\n");return;}pNode fast = *pplist;pNode slow = *pplist;pNode del = NULL; while (fast->next != NULL) //快指针走到表尾时跳出循环{--k; if (k <= 0) //当快指针走了k步后,慢指针开始走,快指针也走{slow = slow->next;}fast = fast->next;}del = slow->next;slow->data = slow->next->data;slow->next = slow->next->next;free(del);}
int GetLinkListLen(pList plist) //求链表的长度,辅助上面代码实现的函数{int count = 0;pNode cur = plist;while (cur != NULL){count++;cur = cur->next;}return count;}
6.逆序打印单链表
void PrintReversely(pList plist);
可能你会觉得逆序打印,就直接把链表用上面逆置链表的方法逆置了然后在打印;这样虽然可以但是很麻烦,因为我们只要逆序打印,所以不用逆置后在打印,如果逆置后在打印反而增加代码量,我们就直接逆序打印,这样就会增加程序的效率。
下面我们看怎么逆序打印单链表:
这里我们要使用递归的思想,看下面这段话
要打印逆序打印,就得先打印最后一个节点,要打印最后一个节点,得先打印倒数第二个节点,要打印倒数第二个节点,得先打印倒数第三个节点,依次类推,要打印正数第二个,就得先打印正数第一个节点。这就是递归这思想,带着这个思想看代码:
void PrintReversely(pList plist){pNode cur = plist;if (cur->next){PrintReversely(cur->next);}printf("%d->", cur->data);}
- 有关单链表的面试题分析及代码实现
- 抽象类的理解及有关面试题
- 有关递归算法的面试题java实现
- 有关线程的面试题
- 有关GC的面试题
- 有关数组的面试题
- 有关Tomcat的面试题
- 有关HashMap的面试题
- java面试题:有关于继承的面试题
- 有关this和super的面试题
- 有关男女比例趋势的面试题
- 转面试题(有关随机数的)
- 有关数据库链接池的面试题
- 跟栈有关的面试题
- 10有关String的经典面试题
- 有关String对象的一个面试题
- 链表有关的常见面试题
- [指针八]有关指针的面试题
- float保留两位小数
- eclipse中查看jdk源码
- C/C++ 打印文件名、行号、函数名的方法
- 【博弈】移棋子游戏(BSOI3969)
- 设计模式之单例模式
- 有关单链表的面试题分析及代码实现
- C++ _deslspec的使用
- 《深入理解java虚拟机-高效并发》读书笔记
- android开发状态栏相关汇总
- 基于freeglut的使用入门 三角形及多个图像绘制
- hadoop集群启动后,两个namenode都是standby
- 学生信息管理系统
- BroadcastReceiver 启动activity(在activity之外启动一个activity)
- 研发流程学习