数据结构之链表编程(C++)

本例中单链表未添加头结点。
结点定义为:

struct ListNode
{
    int data;
    ListNode* next;
    ListNode(int x) :data(x), next(nullptr) {}
};

1 单链表尾部添加结点

void AddToTail(ListNode** pHead, int value)
{
    ListNode* pNew = new ListNode(value);
    if (*pHead == nullptr)
        *pHead = pNew;
    else
    {
        ListNode* pNode = *pHead;
        while (pNode->next != nullptr)
            pNode = pNode->next;
        pNode->next = pNew;
    }
}

2 删除第i个结点,认为链表非空。

bool DeleteNode(ListNode** pHead, int n)
{
    if (n == 1)
    {
        *pHead = (*pHead)->next;
        return true;
    }       
    ListNode* pNode = *pHead;
    int j = 1;
    while (pNode->next && j < n-1)
    {
        pNode = pNode->next;
        j++;
    }

    if (!(pNode->next) || j != n-1)
        return false;

    ListNode* pDelete = pNode->next;
    pNode->next = pDelete->next;
    delete pDelete;
    pDelete = nullptr;

    return true;
}

3 第i个位置前插入结点

bool InSertList(ListNode** pHead, int pos, int value)
{
    ListNode* pNew = new ListNode(value);
    if (pos == 1)
    {
        pNew->next = *pHead;
        *pHead = pNew;
        return true;
    }
    int j = 1;
    ListNode* pNode = *pHead;
    while (pNode && j<pos-1)
    {
        pNode = pNode->next;
        j++;
    }
    if (!pNode || j!=pos-1)
        return false;

    ListNode* pNext = pNode->next;
    pNode->next = pNew;
    pNew->next = pNext;
    return true;
}

4 从尾到头打印链表,通常打印是只读操作。方法一利用栈实现,未修改链表结构。

void PrintListReverse(ListNode* pHead)
{
    stack<int> nodes;
    ListNode* pNode = pHead;
    while (pNode)
    {
        nodes.push(pNode->data);
        pNode = pNode->next;
    }
    while (!nodes.empty())
    {
        cout << nodes.top() << ' ';
        nodes.pop();
    }
}

方法二反转链表,改变了链表结构。打印部分略去。

ListNode* ReverseList(ListNode* pHead)
{
    ListNode* pReverseHead = nullptr;     //建立返回值,简化输入
    ListNode* pPrev = nullptr;
    ListNode* pNode = pHead;

    while (pNode)
    {       
        ListNode* pNext = pNode->next;
        if (!pNext)
            pReverseHead = pNode;        //不立即返回的原因是还要再连接pNode与pPrev

        pNode->next = pPrev;
        pPrev = pNode;
        pNode = pNext;  
    }
    return pReverseHead;
}

5 给定单链表头指针和一个结点(待删除的)指针,o(1)时间内删除该结点
将结点后一位的值赋给前一位,再将后一位删除。要注意的情况有删除的结点是否为尾结点;
链表中是否只有一个结点;并默认待删除的结点位于链表之内。

void DeleteNode(ListNode** pHead, ListNode* pToDelete)
{
    if (!pHead || !pToDelete)
        return;
    if (pToDelete->next)                  //不是尾结点
    {
        ListNode* pNext = pToDelete->next;
        pToDelete->data = pNext->data;
        pToDelete->next = pNext->next;

        delete pNext;
        pNext = nullptr;
    }
    else if (*pHead == pToDelete)         //只有一个结点
    {
        delete pToDelete;
        pToDelete = nullptr;
        *pHead = nullptr;
    }
    else                                  //是尾结点
    {
        ListNode* pNode = *pHead;
        while (pNode->next != pToDelete)
            pNode = pNode->next;
        delete pToDelete;
        pToDelete = nullptr;
        pNode->next = nullptr;
    }
}

6 输入一个链表,输出链表的倒数第k个结点, 从1开始计数。
单链表使用两个指针可以在遍历一遍的情况下,完成任务。

ListNode* FindLastK(ListNode* pHead, int k)
{
    if (!pHead || k < 1)
        return nullptr;
    ListNode* pAhead = nullptr;
    ListNode* pBehind = pHead;
    for (int i=0;i<k-1;++i)
    {
        pBehind = pBehind->next;
        if (!pBehind)
            return nullptr;
    }
    pAhead = pHead;
    while (pBehind->next)
    {
        pAhead = pAhead->next;
        pBehind = pBehind->next;
    }
    return pAhead;
}

7 合并两个排序的链表
输入两个递增排序的链表,合并两个链表使结点仍是递增排序的。举例1–3-5-7,2-4-6-8
1-2-3-4-5-6-7-8. 相同重复的问题考虑递归调用。

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
    if (!pHead1)
        return pHead2;
    else if (!pHead2)
        return pHead1;
    ListNode* pHeadMerge = nullptr;
    if (pHead1->data > pHead2->data)
    {
        pHeadMerge = pHead2;
        pHeadMerge->next = Merge(pHead1, pHead2->next);
    }
    else
    {
        pHeadMerge = pHead1;
        pHeadMerge->next = Merge(pHead1->next, pHead2);
    }
    return pHeadMerge;
}

8 复杂链表的复制。每个结点除了有一个next指针指向下一个结点,还有一个pBling指向链表的任意结点。大问题化分成一个个的小问题。找到影响效率的关键,再找效率高的替代解决方案。

结点定义为:

struct complexListNode
{
    int data;
    complexListNode* next;
    complexListNode* bling;
    complexListNode(int x) :data(x), next(nullptr), bling(nullptr) {};
};

实现的有三部分,1 本来是A-B-C-D, 变为A-A’-B-B’-C-C’-D-D’;

void CloneNodes(complexListNode* pHead)
{
    complexListNode* pNode = pHead;
    while (pNode)
    {
        complexListNode* pNew = new complexListNode(pNode->data);
        pNew->next = pNode->next;
        pNode->next = pNew;
        pNode = pNew->next;
    }
}

2 将原有的A-B-C-D的bling的下一位赋给A’-B’-C’-D’.

void connectPBling(complexListNode* pHead)
{
    complexListNode* pNode = pHead; 
    while (pNode)
    {
        complexListNode* pNodeClone = pNode->next;
        if (pNode->bling)
            pNodeClone->bling = pNode->bling->next;
        pNode = pNodeClone->next;
    }
}

3 拆分两个链表为 A-B-C-D和A’-B’-C’-D’

complexListNode* seperate(complexListNode* pHead)
{
    complexListNode* pNode = pHead;
    complexListNode* pHeadClone = pNode->next;
    complexListNode* pNodeClone = pNode->next;

    while (pNode)
    {   
        pNode->next = pNodeClone->next;
        pNode = pNode->next;
        if (!pNode)      //pNode到达尾部
            break;
        pNodeClone->next = pNode->next;
        pNodeClone = pNodeClone->next;
    }
    return pHeadClone;
}

4 合起来为:

complexListNode* clone(complexListNode* pHead)
{
    if (!pHead)     
        return nullptr;
    CloneNodes(pHead);
    connectPBling(pHead);
    return seperate(pHead);
}

9 输入两个链表,找出它们的第一个公共结点。

ListNode* FindCommonNode(ListNode* pHead1, ListNode* pHead2)
{
    if (!pHead1 || !pHead2)
        return nullptr;
    int length1 = 0, length2 = 0;
    ListNode* pNode = pHead1;
    while (pNode)
    {
        pNode = pNode->next;
        length1++;
    }
    pNode = pHead2;
    while (pNode)
    {
        pNode = pNode->next;
        length2++;
    }
    int lenghtdif = length1 - length2;
    ListNode* pHeadLong = pHead1;
    ListNode* pHeadShort = pHead2;
    if (length1 < length2)
    {
        pHeadLong = pHead2;
        pHeadShort = pHead1;
        lenghtdif = length2 - length1;
    }
    for (int i = 0; i < lenghtdif; ++i)
        pHeadLong = pHeadLong->next;

    while (pHeadLong && pHeadShort && pHeadLong!=pHeadShort)
    {
        pHeadLong = pHeadLong->next;
        pHeadShort = pHeadShort->next;      
    }
    return pHeadLong;
}

10 输入一颗二叉搜索树,将该二叉搜索树转换成排序的双向链表。只能调整树中结点指针的方向。
递归的一个重要思想,子问题已完成。大问题分解成小问题。

struct BinaryTreeNode
{
    int data;
    BinaryTreeNode* left;
    BinaryTreeNode* right;
};


BinaryTreeNode* Convert(BinaryTreeNode* pRoot)
{
    BinaryTreeNode* pLastNode = nullptr;
    ConvertNode(pRoot, &pLastNode);

    BinaryTreeNode* pHead = pLastNode;
    while (pHead && pHead->left)
        pHead = pHead->left;
    return pHead;
}

void ConvertNode(BinaryTreeNode* pRoot, BinaryTreeNode** pLastNode)
{
    if (!pRoot)
        return;
    BinaryTreeNode* pCurrent = pRoot;
    if (pCurrent->left)
        ConvertNode(pRoot->left, pLastNode);

    pCurrent->left = *pLastNode;
    if (*pLastNode)
        (*pLastNode)->right = pCurrent;
    *pLastNode = pCurrent;

    if (pCurrent->right)
        ConvertNode(pRoot->right, pLastNode);
}

11 0,1,….n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求圆圈里剩下的最后一个数字。

int LastNumLeft(int n, int m)
{
    if (n < 1 || m < 1)
        return -1;
    list<int> li;
    for (int i = 0; i < n; ++i)
        li.push_back(i);
    list<int>::iterator it = li.begin();
    while (li.size()>1)
    {
        for (int i=0;i<m-1;++i)
        {
            if (it == li.end())
                it = li.begin();
            else
            {
                it++;
                if (it == li.end())
                    it = li.begin();
            }               
        }
        it = li.erase(it);
        if (it == li.end())          //删除为链表最后一个结点,赋值begin()
            it = li.begin();
    }
    return li.front();
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值