链表相关的热点面试题(一)

先把相关的题目给出来
1. 从尾到头打印单链表
2. 删除一个无头单链表的非尾节点
3. 在无头单链表的一个非头节点前插入一个节点
4. 逆置/反转单链表
5. 单链表排序
6. 合并两个有序链表,合并后依然有序
7. 查找单链表的中间节点, 要求只能遍历一次链表
先把单链表结构给出来

template<typename T>
struct Node
{
    Node<T>* _next;
    T _value;
    Node(const T& value = T())
        :_value(value)
        , _next(NULL)
    {}
};
  • 从尾到头打印单链表
    这就是最简袋的递归思想。
void ReversePrint(const Node<int>* node)
{
    if (node == NULL)
        return;
    ReversePrint(node->_next);
    cout << node->_value << " ";
}
  • 删除一个无头单链表的非尾节点(链表的O(1)删除)
    乍一看是不是有点没头绪,甚至都不知道题目是什么意思。
    一般的我们要删除一个单链表节点pos,都是找到它的前一个节点prev,然后让prev->next指向pos->next,但是这里说要删除一个无头节点,也就是我们没办法通过遍历找到pos的前一个节点,那怎么办呢?
    其实思路也是很简单的,我们可以通过赋值的方式,只对节点数据就行改变,可以把要删除节点pos->next的数据,也就是del->value赋值给pos,然后再删除del这个节点就好了。
    这里写图片描述
void EraseNotTail(Node<int>* node)
{
    assert(node&&node->_next);//去除空节点和尾节点的情况
    Node<int>* del = node->_next;
    node->_value = del->_value;
    node->_next = del->_next;
    delete del;
}

这道题也叫做 链表的O(1)删除。

  • 在无头单链表的一个非头节点前插入一个节点
    刚做完上面那道删除,这又来插入是不是感觉很简单。
    思路是一样的:
    我们先申请一个节点newnode,然后将newnode插入到pos后面,然后交换newnode和pos的数据。
    这里写图片描述
void InsertFrontNode(Node<int>* node)
{
    assert(node);//去除节点为空情况
    Node<int>* newnode = new Node<int>(5);
    newnode->_next = node->_next;
    node->_next = newnode;
    swap(node->_value, newnode->_value);
}
  • 逆置/反转单链表
    这里写图片描述
void Reverse(Node<int>*& head)
{
    assert(head!=NULL);
    Node<int>* newhead = head;
    Node<int>* cur = head->_next;
    head->_next = NULL;
    while (cur)
    {
        Node<int>* tmp = cur->_next;
        cur->_next = newhead;
        newhead = cur;
        cur = tmp;
    }
}
  • 单链表排序( 冒泡排序)
    其实就和普通的冒泡排序一样
void BubbleSort(Node<int>* head)
{
    assert(head);   
    if (head->_next== NULL)
        return;
    Node<int>* cur = head;
    Node<int>* tail = NULL;
    while (cur != tail)     //当尾指针不等于头指针时进行冒泡
    {
        while (cur->_next != tail)   //控制cur指针最后到的位置是倒数第二个节点
        {

            if (cur->_value > cur->_next->_value)
            {
                swap(cur->_value, cur->_next->_value);
            }
            cur = cur->_next;
        }
        tail = cur;
        cur = head;
    }
}
  • 合并两个有序链表,合并后依然有序
    这里是用递归的思想,每次进来都找数据小的那个节点,让小的节点最为新的头节点,知道有一个链表走到NULL位置。
Node<int>* MergeTwoList(Node<int>*& head1, Node<int>*& head2)
{
    if (head1 == NULL)
        return head2;
    if (head2 == NULL)
        return head1;
    Node<int>* newhead = head1->_value < head2->_value ? head1 : head2;
    if (newhead == head1)
        newhead->_next = MergeTwoList(head1->_next, head2);
    if (newhead == head2)
        newhead->_next = MergeTwoList(head1, head2->_next);
    return newhead;
}
  • 查找单链表的中间节点, 要求只能遍历一次链表
    这个就是快慢指针,快指针一次走两步,慢指针一次走一步。
    最后快指针fast走到尾节点的时候,慢指针刚好到中间节点。
    这里写图片描述
Node<int>* FindMidNode( Node<int>* head)
{
    assert(head);
    Node<int>* slow= head;
    Node<int>* fast = head;
    while (fast != NULL)
    {
        if(fast->next==NULL)
            break;
        fast=fast->_next->_next;
        slow = slow->_next;
    }
    return slow;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值