链表是一种最简单的数据结构之一,经常会被面试官用来考察应聘者的基础扎不扎实,最近也到了求职季,所以我把自己对链表的一些理解写出来,希望能跟大家交流交流;

  链表的概念其实挺简单,无非就是一个利用指针将数据元素顺序串联起来的一种非连续、非顺序的存储结构;链表中每一个结点都包含两个部分:一个室存储数据的数据单元,另一个是存储下一个结点的指针;链表的数据结构实现如下所示:

struct listnode{
int data;
struct listnode next; //c里面必须加上struct,c++里面不需要;当然也可以通过typedef为结构定义一个别名
};

本片文章总结的链表相关题目有:

题目一:求在O(1)时间复杂度内删除改结点?

题目二:链表中结点的个数?

题目三:链表是否存在环?

题目四:查找链表中的倒数第K个结点并返回?

题目五:查找单链表中间结点?

题目六:从尾到头打印链表?

题目七:已知两个结点各自有序,把它们合并成一个有序的链表?

题目八:判断两个链表是否相交?如果相交返回相交的第一个结点?

题目九:一直一个链表存在环,求进入换的第一个结点?

题目十:将链表反转?

下面是问题详解:

题目一:求在O(1)时间复杂度内删除改结点?

这道题乍一看很简单,删除结点找到该节点然后删除不就行了,但是题目要求的是时间复杂度要控制在O(1)内,好像有点困难,但其实也简单,只要用改结点的下一个结点覆盖该结点,然后删除该结点的下一个结点即可;

当然在删除的时候要分两种情况,一种是删除最后一个结点,一种是删除其余结点,删除最后一个结点时因为涉及到最后链表结尾的关系,所以复杂度为O(n),删除其他节点复杂度为O(1);

void deletenode(listnode *head , listnode *temp){
if(!temp)return ; if(temp->next){ //删除其他结点
temp->val = temp->next->val;
ListNode * temp = temp->next;
temp->next = temp->next->next;
delete temp;
}
else{ // 要删除的是最后一个节点
if(head == temp){ // 链表中只有一个节点的情况
head = NULL;
delete temp;
}
else{
ListNode * pNode = head;
while(pNode->next != temp) // 找到倒数第二个节点
pNode = pNode->next;
pNode->next = NULL;
delete temp;
}
}
}

题目二:链表中结点的个数?

  这道题算是最简单的了,在时间复杂度O(n)内遍历整个链表就可以得到链表的数量;代码参考:

unsigned int getnumoflist(listnode *head){
if(!head)return 0;
listnode* tempnode = head;
if(havecir(tempnode ))
return -1;
else{
unsigned int ret = 0;
while(tempnode){
++ret;
tempnode = tempnode->next;
}
return ret;
}
}

  注意,一般很多网上看到的代码会有判断输入是否为NULL的情况;

  但是也有一种情况需要注意,就是输入的链表存在环的情况,如果有环不判断的话while会陷入死循环,所以必须提前判断下,在面试的时候提出来应该会好一点,havecir函数会在下面介绍;

题目三:链表是否存在环?

  这一题承接上一题,一般判断环的方法是设置快慢指针,快指针一步走两个结点,慢指针一步走一个结点,如果存在环,那么快慢指针必然会在环内某一点相遇,否则没有环就会退出;

bool havecircle(listnode *head){
if(!head) return false;
listnode *pfast = head;
listnode *pslow = head; while(pfast && pslow){
pfast = pfast->next->next;
pslow = pslow->next;
if(pfast == pslow)
return true;
}
return false;
}

题目四:查找链表中的倒数第K个结点并返回?

  解法是设置前后结点,前结点先想向前走k-1步,然后前后结点一块向后走,等前结点到达最后一个结点时,后结点就是倒数第k个结点了;

listnode* findrkthnode(listnode *head , int k){
if(!k || !head) return NULL; listnode* frontnode = head;
lisenode* backnode = head;
while(k > 1 && frontnode){
--k;
frontnode = frontnode->next;
} if(k > 1 || !frontnode)
return NULL; while(frontnode->next){
backnode = backnode->next;
frontnode = frontnode->next;
}
return backnode;
}

题目五:查找单链表中间结点?

  跟判断环的时候一样设置快慢指针,当快指针到达结尾的时候,慢指针就是中间结点了;在代码里面我也加了判断是否为环的情况;

listnode* getmidnode(listnode *head){
if(!head || !head->next) return head; if(havecircle(head)) return NULL; listnode *pfast = head;
listnode *pslow = head;
while(pfast->next){
pfast = pfast->next;
pslow = pslow->next;
if(pfast->next)
pfast = pfast->next;
}
return pslow;
} 

题目六:从尾到头打印链表?

这道题很简单,可以使用递归或者利用栈保存结点并反向直接输出即可;递归算法代码量较少,理解起来相对容易;

递归算法:

void printlist1(listnode *head){
if(!head){
return;
}
else{
printlist(head->next);
printf("%d->" , head->data);
}
}

利用栈的算法:

void printlist2(listnode *head){
stack<int> s;
while(head){
s.push(head->data);
head = head->next;
} while(!s.empty()){
printf("%d->" , s.top());
s.pop();
}
}

题目七:已知两个结点各自有序,把它们合并成一个有序的链表?

这道题在leetcode上面有,我直接把在上面提交的代码贴出来吧;

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(!l1) return l2;
if(!l2) return l1;
ListNode *result=NULL;
ListNode *temp=NULL;
ListNode *node=NULL;
int fir=1;
while(l1&&l2){
if(l1->val>l2->val){
temp=l2;
l2=l2->next;
}
else{
temp=l1;
l1=l1->next;
}
if(fir){
result=temp;
fir=0;
}
else
node->next=temp;
node=temp;
}
if(l1)
node->next=l1;
if(l2)
node->next=l2;
return result;
}

当然,这道题也有自己的递归算法:

ListNode * mergeTwoLists2(ListNode * head1, ListNode * head2)
{
if(!head1)
return head2;
if(!head2)
return head1;
ListNode * pHeadMerged = NULL;
if(head1->val < head2->val)
{
pHeadMerged = head1;
pHeadMerged->next = mergeTwoLists2(head1->next, head2);
}
else
{
pHeadMerged = head2;
pHeadMerged->next = mergeTwoLists2(head1, head2->next);
}
return pHeadMerged;
}

题目八:判断两个链表是否相交?如果相交返回相交的第一个结点?

  首先确定相交,如果两个结点相交那么最后一个结点肯定是公用的,所以只要简单遍历两个链表,然后记住最后一个结点,相比较,如果一样则相交,否则不想交;

bool isintersected(listnode * head1, listnode * head2){
if(!head1 || !head2) return false; listnode *last1 = head1;
listnode *last2 = head2; while(last1->next)
last1 = last1->next;
while(last2->next)
last2 = last2->next; return last1 == last2;
}

  默认两个链表确定相交,返回相交的第一个结点,只需要知道两个链表的长度,然后将长链表想前走k步使得其与段链表长度相同,然后同步向下走,若节点相同就返回;

listnode * isintersected(listnode * head1, listnode * head2){
if(!head1 || !head2) return NULL; int l1 = 0 , l2 = 0 , l;
listnode *tempnode1 = head1;
listnode *tempnode2 = head2; while(tempnode1->next){
tempnode1 = tempnode1->next;
++l1;
}
while(tempnode2->next){
tempnode2 = tempnode2->next;
++l2;
}
tempnode1 = head1;
tempnode2 = head2;
if(l1 > l2){
l = l1 - l2;
while(l > 0){
tempnode1 = tempnode1->next;
--l;
}
}
else if(l2 > l1){
l = l2 - l1;
while(l > 0){
tempnode2 = tempnode2->next;
--l;
}
}
while(tempnode1->val != tempnode2->val){
tempnode1 = tempnode1->next;
tempnode2 = tempnode2->next;
}
return tempnode;
}

  

题目九:一个链表存在环,求进入换的第一个结点?

  首先找到链表中的任一结点,然后将其下一结点指向NULL,这样就可以转化为求两个相交链表相交的第一个结点的问题了;

listnode* GetFirstNodeInCircle(listnode * pHead)
{
if(!pHead || !pHead->next)
return NULL; listnode * pFast = pHead;
listnode * pSlow = pHead;
while(pFast && pFast->next)
{
pSlow = pSlow->next;
pFast = pFast->next->next;
if(pSlow == pFast)
break;
}
if(!pFast || !pFast->next)
return NULL; // 将环中的此节点作为假设的尾节点,将它变成两个单链表相交问题
listnode * pAssumedTail = pSlow;
listnode * pHead1 = pHead;
listnode * head = pAssumedTail->next; int l1 = 0 , l2 = 0 , l;
listnode *tempnode1 = pHead1;
listnode *tempnode2 = head; while(tempnode1->next){
tempnode1 = tempnode1->next;
++l1;
}
while(tempnode2->next){
tempnode2 = tempnode2->next;
++l2;
}
tempnode1 = pHead1;
tempnode2 = head;
if(l1 > l2){
l = l1 - l2;
while(l > 0){
tempnode1 = tempnode1->next;
--l;
}
}
else if(l2 > l1){
l = l2 - l1;
while(l > 0){
tempnode2 = tempnode2->next;
--l;
}
}
while(tempnode1->val != tempnode2->val){
tempnode1 = tempnode1->next;
tempnode2 = tempnode2->next;
}
return tempnode;
}

题目三:将链表反转?

  存在两种解法,一种是利用循环,一种是利用递归,通常情况下推荐使用循环,因为递归太深容易造成堆栈的溢出,并且调用函数要消耗资源,所以递归空间和时间消耗都大;

  循环解法:从头到尾循环一个一个遍历结点,逆序链表;

listnode *reverselist1(listnode *head){
if(!head || !head->next) return head; listnode *retnode = NULL; //反转后的新链表头指针,
listnode *pCurrent = phead;
listnode *ptemp = NULL;
while(pCurrent){
ptemp = pCurrent;
pCurrent = pCurrent->next;
ptemp->next = retnode;
retnode = ptemp;
}
return retnode;
}

   递归解法:递归到链表的结尾,然后返回时逆序链表;

listnode *reverselist2(listnode *head){
if(!head)return NULL; listnode *tempnode = head;
if(head && head->next){
listnode *retnode = reverse(head);
tempnode = NULL;
}
return retnode;
} listnode *reverse(listnode *head){
listnode *retnode = NULL;
if(head)
retnode = reverselist2(head->next); if(head && head->next){
if(!retnode)
retnode = head->next;
head->next = head;
}
return retnode;
}

链表各种操作及其实现方法(c实现)的更多相关文章

  1. 程序员面试必备-链表各种操作及其实现方法(c实现)

    链表是一种最简单的数据结构之一,经常会被面试官用来考察应聘者的基础扎不扎实,最近也到了求职季,所以我把自己对链表的一些理解写出来,希望能跟大家交流交流: 链表的概念其实挺简单,无非就是一个利用指针将数 ...

  2. C语言之----面向对象的方法实现链表的操作

    1 /* 2 * 详细运行过程: 本程序实现的是对链表的简单的操作,即链表的增 删 改 查 销毁 初始化 3 * 运用面向对象的思想,实现一个类op,op中包括了所有的链表操作方法 4 * 其他的程序 ...

  3. java实现单链表常见操作

    一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashM ...

  4. java对单向单向链表的操作

    概述:众所周知,数据对于数据的存储时连续的,也就是说在计算机的内存中是一个整体的.连续的.不间断的ADT数据结构.伴随的问题也会随之出现,这样其实对于内存的动态分配是不灵活的.而链表具备这个优点.因此 ...

  5. 一些ES5的操作数组的方法

    在ES5规范中新增了不少操作数组的方法,特此罗列一下以备使用 1. forEach循环 有点类似jQuery的each循环 [12,23,36,4,5].forEach(function(v,k){ ...

  6. JavaScript操作JSON的方法总结,JSON字符串转换为JSON对象

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意 ...

  7. android操作线程各种方法解析

    (一)刚开始学习android的时候我是这么写的 new Thread( new Runnable() { public void run() { myView.invalidate(); } }). ...

  8. C#操作内存读写方法的主要实现代码

    C#操作内存读写方法是什么呢?让我们来看看具体的实例实现: using System.Runtime.InteropServices; using System.Text; publicclass F ...

  9. VBS操作Excel常见方法

    VBS操作Excel常见方法 作者: 字体:[增加 减小] 类型:转载 时间:2009-11-13我要评论 VBS控制Excel常见方法,需要的朋友可以参考下. dim oExcel,oWb,oShe ...

随机推荐

  1. 零基础学css

    选择器:标签选择器.id选择器.类选择器 ---------------------------------------------------------------------------- 标签 ...

  2. PHP.34-TP框架商城应用实例-后台10-商品分类-需求分析、创建无限级商品分类,递归

    商品管理需求分析 1.实现商品无限级分类管理[类似京东三级分类] 2.添加商品时要指定商品属于一个主分类和多个扩展分类[扩展分类可以是其他主分类] 3.商品列表中可以根据分类搜索商品 a) 搜索一个分 ...

  3. 1,MongoDB简介和安装

    一.初识MongoDB MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数 ...

  4. 使用Matrix-tree与它的行列式来解决生成树计数问题

    我又把Matrix写错啦 这东西讲课的时候竟然一笔带过了,淦 好吧这东西我不会证 那我们来愉快的看结论吧 啦啦啦 预备工作 你有一个 $ n $ 个点的图 比如说 5 /|\ / | \ 2--1-- ...

  5. css一些事儿

    1. margin和padding 如果边界画一条线,则margin的属于边界外,padding属于边界内 当我们给元素背景色时,margin区域不会被着色,而padding区域会被着色. 当上下两个 ...

  6. 【转载】Unity3D研究院之IOS自定义游戏摇杆与飞机平滑的移动

    移动开发游戏中使用到的触摸游戏摇杆在iPhone上是非常普遍的,毕竟是全触摸屏手机,今天MOMO 通过一个小例子和大家讨论Unity3D 中如何自定义一个漂亮的全触摸游戏摇杆.        值得高兴 ...

  7. AppCan试用体验

    最近自己想开发一个基于Android平台的小应用,但不想使用JAVA开发,还要快速实现功能,学习成本低. 所以找了很多框架,最后基本锁定在phoneGap和AppCan,又看了AppCan与phone ...

  8. 融合模型Aggregation

    从一堆弱分类器融合得到强分类器. 比如假设现在你只能水平或竖直线分割,那么无论如何都分不好,但是假设组合三次分割,就会得到如图所示的一个较好的分割线. 再比如,PLA 融合后有large margin ...

  9. 通过命令行安装或卸载Tomcat服务

    一.安装Tomcat服务 1.打开命令提示符 方法1: 按住win+R,打开运行,输入cmd,打开命令提示符 方法2:在开始菜单>所有程序>附件>命令提示符 2. 通过命令进入到to ...

  10. POJ 2142 The balance | EXGCD

    题目: 求ax+by=c的一组解,使得abs(x)+abs(y)尽量小,满足前面前提下abs(ax)+abs(by)尽量小 题解: exgcd之后,分别求出让x尽量小和y尽量小的解,取min即可 #i ...