链表专题——面试中常见的链表问题

声明:链表定义如下:

//Java:
class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
//C++:
typedef struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
}ListNode;

?

从无头单链表中删除节点

详情:给定一个没有头指针的单链表,一个指针指向此单链表中间的一个节点(不是第一个,也不是最后一个节点),请将该节点从单链表中删除。
题解:
解法一:由于单链表并没有给出头指针,因此我们无法通过遍历链表的方式找到该节点的前一个节点来改变其 next 指向去指向该节点的 next 节点。换一种思路,我们可以将该节点的元素值全部替换成其 next 节点,然后删除 next 节点,这样就相当于把该节点删除了。

//Java
public void deleteRandomNode(ListNode currentNode) {
    ListNode nextNode = currentNode.next;
    if (nextNode != null) {
        currentNode.val = nextNode.val;
        currentNode.next = nextNode.next;
    }
    nextNode = null;
}
//C++
void deleteRandomNode(ListNode *current){
    ListNode *next = current->next;
    if (next != NULL){
        current->val = next->val;
        current->next = next->next;
    }
    delete next;
}

?

反转链表

详情:给定一个链表的头指针,要求只遍历一次,将单链表中的元素顺序反转过来。
题解:
解法一:题目较为简单,每次反转的时候记录下一个节点的指针

//Java
public ListNode ReverseList(ListNode head) {
    ListNode pre = null, next = null;
    while (head != null) {
        next = head.next;
        head.next = pre;
        pre = head;
        head = next;
    }
    return pre;
}
//C++
ListNode *ReverseList(ListNode *pHead) {
    ListNode *current = NULL, *prev = NULL;
    while (pHead != NULL) {
        current = pHead;
        pHead = pHead->next;
        current->next = prev;
        prev = current;
    }
    return current;
}

?

两个链表的第一个公共节点

详情:输入两个链表,找出它们的第一个公共节点
题解:
解法一:为了找到两个链表的公共节点,那么我们可以从尾往头遍历查找,但是只给了我们头节点,因此类似于栈的先进后出,因此我们可以用两个栈来保存节点,然后从栈中取出节点进行比较。
解法二:统计两个链表的长度 len1 和 len2,让较长的链表先走·abs(len1 - len2)`长度,之后二者同时继续往下遍历,查找第一个公共节点。

//C++
ListNode *FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
    int len1 = SizeLinkedList(pHead1);
    int len2 = SizeLinkedList(pHead2);

    if (len1 > len2) {
        pHead1 = walker(pHead1, len1 - len2);
    } else {
        pHead2 = walker(pHead2, len2 - len1);
    }

    while (pHead1->val != pHead2->val) {
        pHead1 = pHead1->next;
        pHead2 = pHead2->next;
    }

    return pHead1;
}

int SizeLinkedList(ListNode *head) {
    if (head == NULL)   return 0;
    int size = 0;
    ListNode *current = head;
    while (current != NULL) {
        size++;
        current = current->next;
    }
    return size;
}

ListNode *walker(ListNode *head, int cnt) {
    while (cnt--) {
        head = head->next;
    }
    return head;
}

?

判断给定链表是否存在环

详情:给定一个链表,判断这个链表是否存在环
题解:
解法一:Floyd判圈算法

//C++
bool hasRing(ListNode *pHead){
    bool hasRing = false;
    ListNode *fast = pHead, *slow = pHead;
    while (fast != NULL && fast->next != NULL){
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)   hasRing = true;
    }
    return hasRing;
}

?

链表中环的入口节点

详情:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
题解:
解法一:Floyd判圈算法

//Java
public ListNode EntryNodeOfLoop(ListNode pHead) {
    ListNode fast = pHead, slow = pHead;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow)   break;
    }
    if (fast == null || fast.next == null)  return null;
    fast = pHead;
    while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
    }

    return fast;
}
//C++
ListNode *EntryNodeOfLoop(ListNode *pHead) {
    ListNode *slow = pHead, *fast = pHead;
    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)   break;
    }
    if (fast == NULL || fast->next == NULL)   return NULL;
    fast = pHead;
    while (fast != slow) {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

?

判断两个链表是否相交

详情:给定两个单链表的头指针,判断这两个链表是否相交。
题解:
解法一:若两个链表相交,则链表的最后一个节点一定是公共的,因此可以利用这个性质求解。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
    if (pHead1 == NULL || pHead2 == NULL)   return false;
    while (pHead1->next != NULL)    pHead1 = pHead1->next;
    while (pHead2->next != NULL)    pHead2 = pHead2->next;
    if (pHead1 == pHead2)   return true;
    return false;
}

解法二:由于都是单项链表,也就是都没有环,那么我们可以把第一个链表链接到第二个链表后面,如果新的链表有环,证明了有公共节点。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
    if (pHead1 == NULL || pHead2 == NULL)   return false;
    pHead1->next = pHead2;
    return hasRing(pHead1);
}

?

判断两个链表是否相交变形

详情:给定两个有环链表的头指针,判断这两个链表是否相交。
题解:
解法一:对于有环链表,如果相交,存在以下几种情况:

因此,找到链表的入口节点,判断是否相等,对应情形一和二,对于三,我们可以固定一个节点,然后遍历链表来判断是否存在相交。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
    if (pHead1 == NULL || pHead2 == NULL)   return false;
    ListNode *entry1 = EntryNodeOfLoop(pHead1);
    ListNode *entry2 = EntryNodeOfLoop(pHead2);

    if (entry1 == entry2)   return true;
    else{
        ListNode *backup = entry2;
        do
        {
            entry2 = entry2->next;
        }while (entry2 != entry1 && entry2 != backup);
        return entry2 != backup;
    }
}

?

合并两个排序的链表

详情:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
题解:
解法一:

//Java
public ListNode Merge(ListNode list1, ListNode list2) {
    if (list1 == null) {
        return list2;
    }
    if (list2 == null) {
        return list1;
    }

    ListNode prev = null;
    ListNode root = list1.val < list2.val ? list1 : list2;
    while (list1 != null && list2 != null) {
        if (list1.val < list2.val) {
            if (prev == null) {
                prev = list1;
            } else {
                prev.next = list1;
                prev = list1;
            }
            list1 = list1.next;
        } else {
            if (prev == null) {
                prev = list2;
            } else {
                prev.next = list2;
                prev = list2;
            }
            list2 = list2.next;
        }
    }
    while (list1 != null) {
        prev.next = list1;
        prev = list1;
        list1 = list1.next;
    }
    while (list2 != null) {
        prev.next = list2;
        prev = list2;
        list2 = list2.next;
    }

    return root;
}
//C++
ListNode *Merge(ListNode *pHead1, ListNode *pHead2) {
    if (pHead1 == NULL) return pHead2;
    if (pHead2 == NULL) return pHead1;
    ListNode *prev = NULL;
    ListNode *root = pHead1->val < pHead2->val ? pHead1 : pHead2;
    while (pHead1 != NULL && pHead2 != NULL) {
        if (pHead1->val < pHead2->val) {
            if (prev == NULL) {
                prev = pHead1;
            } else {
                prev->next = pHead1;
                prev = pHead1;
            }
            pHead1 = pHead1->next;
        } else {
            if (prev == NULL) {
                prev = pHead2;
            } else {
                prev->next = pHead2;
                prev = pHead2;
            }
            pHead2 = pHead2->next;
        }
    }

    while (pHead1 != NULL) {
        prev->next = pHead1;
        prev = pHead1;
        pHead1 = pHead1->next;
    }
    while (pHead2 != NULL) {
        prev->next = pHead2;
        prev = pHead2;
        pHead2 = pHead2->next;
    }
    return root;
}

原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/Linked-list.html

时间: 2024-10-01 12:53:14

链表专题——面试中常见的链表问题的相关文章

前端面试中常见的几个问题

在前端很少有机会接触到算法,大多都交互性的操作,然而从各大公司面试来看,算法依旧是考察的一方面.下面这篇文章就给大家总结了在前端JS面试中常见的测试题问题,有需要的朋友们可以参考借鉴,下面来一起看看吧. 学习数据结构与算法对于工程师去理解和分析问题都是有帮助的.如果将来当我们面对较为复杂的问题,这些基础知识的积累可以帮助我们更好的优化解决思路.下面罗列在前端面试中经常撞见的几个问题吧.image.png1.介绍js的基本数据类型 Undefined.Null.Boolean.Number.Str

程序员面试中常见10大算法汇总

以下是在编程面试中排名前10的算法相关的概念,我会通过一些简单的例子来阐述这些概念.由于完全掌握这些概念需要更多的努力,因此这份列表只是作为一个介绍.本文将从Java的角度看问题,包含下面的这些概念: 1. 字符串 如果IDE没有代码自动补全功能,所以你应该记住下面的这些方法. 1 2 3 4 5 6 toCharArray() // 获得字符串对应的char数组 Arrays.sort() // 数组排序 Arrays.toString(char[] a) // 数组转成字符串 charAt(

面试中常见算法1

Problem 1 : Is it a loop ? (判断链表是否有环?) Assume that wehave a head pointer to a link-list. Also assumethat we know the list is single-linked. Can you come up an algorithm to checkwhether this link list includes a loop by using O(n) time and O(1) space

iOS面试中常见的算法题目

一.前言 这里是在iOS求职中自己遇到的算法题,希望对大家有所帮助.不定期更新.如果大家想在线运行代码调试,可以将代码拷贝到这里.然后进行调试.下面就是常见的算法题目. 二.正文 1.就n的阶乘. 思路:这里面用递归实现 #include <stdio.h> int getNJ(int n) { if (n==1 || n==0) { return 1; } return n*getNJ(n-1); } int main() { printf("%d",getNJ(10))

面试中常见的算法之Java中的递归

1.方法定义中调用方法本身的现象2.递归注意实现 1) 要有出口,否则就是死递归 2) 次数不能太多,否则就内存溢出 3) 构造方法不能递归使用3.递归解决问题的思想和图解: 分解和合并[先分解后合并] 1. 常见的斐波那契数列 1,1,2,3,5,8,13,21,...特征: 从第三个数开始,每个数是前两个数的和. int count = 0; private int getFibo(int i) { if (i == 1 || i == 2) { count = count+1; Syste

HTML5面试中常见的十个问题

1.新的 HTML5 文档类型和字符集是? HTML5 文档类型很简单:<!doctype html> HTML5 使用 UTF-8 编码示例:<meta charset="UTF-8″> 2.HTML5 废弃了哪些 HTML4 标签? HTML5 废弃了一些过时的,不合理的 HTML 标签: frame frameset noframe applet big center basefront 3.HTML5 有哪些新增的表单元素? HTML5 新增了很多表单元素让开发者

面试大总结之一:Java搞定面试中的链表题目

链表是面试中常考的,本文参考了其它一些文章,加上小编的自己总结,基本每个算法都测试并优化过. 算法大全(1)单链表 中还有一些链表题目,将来也会整理进来. * REFS: * http://blog.csdn.net/fightforyourdream/article/details/16353519 * http://blog.csdn.net/luckyxiaoqiang/article/details/7393134 轻松搞定面试中的链表题目 * http://www.cnblogs.co

编程面试过程中常见的10大算法(转)

以下是在编程面试中排名前10的算法相关的概念,我会通过一些简单的例子来阐述这些概念.由于完全掌握这些概念需要更多的努力,因此这份列表只是作为一个介绍.本文将从Java的角度看问题,包含下面的这些概念: 1. 字符串 如果IDE没有代码自动补全功能,所以你应该记住下面的这些方法. toCharArray() // 获得字符串对应的char数组 Arrays.sort() // 数组排序 Arrays.toString(char[] a) // 数组转成字符串 charAt(int x) // 获得

常见的链表排序(Java版)

上篇博客中讲解了九大内部排序算法,部分算法还提供了代码实现,但是那些代码实现都是基于数组进行排序的,本篇博客就以链表排序实现几种常见的排序算法,以飨读者. 快速排序的链表实现 算法思想:对于一个链表,以head节点的值作为key,然后遍历之后的节点,可以得到一个小于key的链表和大于等于key的链表:由此递归可以对两个链表分别进行快速.这里用到了快速排序的思想即经过一趟排序能够将小于key的元素放在一边,将大于等于key的元素放在另一边. 代码实现: 1 //快速排序 2 public stat