zoukankan      html  css  js  c++  java
  • 链表

    单向链表:查找第i个结点时只能从头结点开始,复杂度为O(n)。

    题目:链表的创建,结点插入,删除结点,反转链表,倒数第k个结点...

    1.向链表的尾部插入结点

    typedef struct ListNode{int m_nvalue;ListNode* m_pNext;};
    //往链表末尾中添加节点
    void AddToTail(ListNode** pHead,int value)     //特别注意pHead是一个指向指针的指针。因为往空链表中插入一个结点时,头指针是新插入的结点。此时头指针发生了改动,因此必须设置为指向指针的指针,否则出了该函数之后pHead仍然是一个空指针
    {
        ListNode *pNew=new ListNode();
        pNew->m_nvalue=value;
        pNew->m_pNext=NULL;
        if(*pHead==NULL)   
        {
            *pHead=pNew;
        }
        else                              
        {
            ListNode* pNode=*pHead;
            while(pNode->m_pNext!=NULL)
                pNode=pNode->m_pNext;
            pNode->m_pNext=pNew;
        }
    }

    2.删除结点

    可以在O(1)复杂度下删除结点,最坏情况(删除的是尾结点)复杂度为O(n)。

    //在链表中找到第一个含有某值的结点,并删除它。(只能从头向尾遍历,算法复杂度O(n),不能像数组那样采用索引)
    void RemoveNode(ListNode**pHead,int value)
    {
        if(pHead==NULL||*pHead==NULL)     //*pHead==NULL表示空表  phead==NULL表示该指针未定义
            return;
        ListNode* ToBeDeleted=NULL;
        if((*pHead)->m_nvalue==value)  //如果头指针所指向的结点就是要删除的结点,则把头指针指向下一个结点,把头结点赋值为要被删除的点ToBeDeleted
        {
            ToBeDeleted=*pHead;          
            *pHead=(*pHead)->m_pNext;
        }
        else                          //否则就去判断下一个结点是否应该被删除
        {
            ListNode* pNode=*pHead;
            while(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nvalue!=value)   //下一个结点不是要删除的结点,继续往后移动
            {
                pNode=pNode->m_pNext;
            }
            if(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nvalue==value)    //否则把下一个结点赋值为ToBeDeleted
            {
                ToBeDeleted=pNode->m_pNext;
                pNode->m_pNext=pNode->m_pNext->m_pNext;     //当前结点的指针指向下一个结点的下一个结点
            }
        }
        if(ToBeDeleted!=NULL)                 //找到了应该删除的结点时就删掉,否则结束寻找
        {
            delete ToBeDeleted;
            ToBeDeleted=NULL;
        }
    }
    //在O(1)时间之内删除链表结点
    void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted)
    {
        if(!pListHead||!pToBeDeleted)
            return;
        //要删除的不是尾结点。如果要删除的是结点i,
        if(pToBeDeleted->m_pnext!=NULL)
        {
            ListNode* pNext=pToBeDeleted->m_pnext;
            pToBeDeleted->m_key=pNext->m_key;
            pToBeDeleted->m_pnext=pNext->m_pnext;
            delete pNext;
            pNext=NULL;
        }
        //链表只有一个结点,删除头结点
        else if(*pListHead==pToBeDeleted)
        {
            delete pToBeDeleted;
            pToBeDeleted=NULL;
            *pListHead=NULL;
        }
        //链表中有多个结点,删除尾结点
        {
            ListNode* pNode=*pListHead;
            while(pNode->m_pnext!=pToBeDeleted)
            {
                pNode=pNode->m_pnext;
            }
            pNode->m_pnext=NULL;
            delete pToBeDeleted;
            pToBeDeleted=NULL;
        }
    }

    3.从尾到头打印结点,不改变链表结构

    //从尾到头打印链表,不改变链表结构
    void PrintListReverse(ListNode**pHead)
    {
        if(pHead==NULL||*pHead==NULL)
            return;
        ListNode* pNode=*pHead;
        vector<int> v_list;
        v_list.insert(v_list.begin(),pNode->m_nvalue);
        while(pNode->m_pNext!=NULL)
        {
            pNode=pNode->m_pNext;
            v_list.insert(v_list.begin(),pNode->m_nvalue);
        }
        vector<int>::iterator it=v_list.begin();
        while(it!=v_list.end())     //对的
            printf("%d ",*it++);
        /*while(it++!=v_list.end())   //这样写是从第2个数开始输出,并且指针溢出了。只有在for循环里面才保证循环体内的语句执行完了之后再自增,在while的判断里面是判断语句执行完了就自增。
            printf("%d ",*it);*/
        /*while(it++!=v_list.end())     //这样写是从第2个数开始输出,指针没有溢出。
            printf("%d ",*it);*/
    }

    4.链表结构的反转

    //链表结构的反转
    void ListReverse(ListNode**pHead)
    {
        if(pHead==NULL||*pHead==NULL||(*pHead)->m_pNext==NULL)
            return;
        ListNode* p=*pHead;
        ListNode* q=*pHead;
        while(p->m_pNext!=NULL)
        {
            *pHead=p->m_pNext;
            p->m_pNext=(*pHead)->m_pNext;
            (*pHead)->m_pNext=q;
            q=*pHead;
        }
    }
    void main()
    {
        ListNode** pHead=new ListNode*;    //不赋值,就会出现指针未初始化的错误.原因是指针未赋值时,其值并不是0,而是随便指向了一处内存,当改变了此处内存的值时,可能会带来灾难性的错误
        *pHead=NULL;
        int print_value,remove_value;
        for(int i=0;i<7;i++)
        {
            cin>>print_value;
            AddToTail(pHead,print_value);
        }
        PrintListReverse(pHead);
        cin>>remove_value;
        RemoveNode(pHead,remove_value);
        ListReverse(pHead);
        PrintListReverse(pHead);
    }

     5.面试题:找到两个链表的第一个公共结点——剑指offer面试题37

    思路1:用2个栈存储两个链表,从栈顶开始比较,相同的移除,移除的最后一个相同的结点就是两个链表的第一个公共结点。

    思路2:首先分别遍历两个链表的长度,求得长度差k。设置2个指针,较长链表的指针先行k步,然后比较2个指针的值是否相同,如果不同,2个指针同时后移,直到找到第一个相同的指针为止。

    思路1的代码:

    //输入两个链表,找到他们第一个公共结点
    #include<stdlib.h>
    #include<stdio.h>
    #include<stack>
    #include<iostream>
    using namespace std;
    
    struct ListNode
    {
        int m_key;
        ListNode* m_pnext;
    };
    
    ListNode* findPublic(ListNode* const list1,ListNode* const list2)   //指针值不能变   如果写成const ListNode* ,代表的是元素值不能变
    {
        ListNode* PublicNode=new ListNode;
        if(list1==NULL||list2==NULL)
            return NULL;     //这样写行吗?
        ListNode* plist1,*plist2;
        plist1=list1;    //不能将const ListNode*分配到ListNode*类型的实体
        plist2=list2;
        stack<ListNode*> stack1,stack2;
        while(plist1!=NULL)                  //把链表压栈
        {
            stack1.push(plist1);
            plist1=plist1->m_pnext;
        }
        while(plist2!=NULL)
        {
            stack2.push(plist2);
            plist2=plist2->m_pnext;
        }
        while(stack1.top()==stack2.top())    //反过来比较,直到找到最后一个重合的值为止
        {
            PublicNode=stack1.top();
            stack1.pop();
            stack2.pop();
        }
        return PublicNode;
    }
    
    //生成一个链表的写法
    void inputList(ListNode* &pfirst)    //初始化时,要写成ListNode* pfirst=NULL
    {
        int p;
        ListNode* currentNode=NULL;
        while(cin>>p)
        {
            if(p==-1)
                break;
            ListNode* newNode=new ListNode;
            newNode->m_key=p;
            newNode->m_pnext=NULL;
            if(pfirst==NULL)               
            {
                pfirst=newNode;
                currentNode=pfirst;
            }
            else
            {
                currentNode->m_pnext=newNode;
                currentNode=currentNode->m_pnext;
            }
        }
    }

     6.合并2个排序的链表——剑指offer面试题17

    //合并2个排序的链表
    ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
    {
        if(pHead1==NULL)
            return pHead2;
        else if(pHead2==NULL)
            return pHead1;
        ListNode* pMergedHead=NULL;
        if(pHead1->m_key<pHead2->m_key)
        {
            pMergedHead=pHead1;
            pMergedHead->m_pnext=Merge(pHead1->m_pnext,pHead2);
        }
        else
        {
            pMergedHead=pHead2;
            pMergedHead->m_pnext=Merge(pHead1,pHead2->m_pnext);
        }
        return pMergedHead;
    }
  • 相关阅读:
    sql查询原理和Select执行顺序(sql优化)
    SQL Server如何清除连接过的服务器名称历史?
    SQL Server DATEDIFF()
    oracle 创建用户、表空间
    IDEA 快捷键
    (Reflection)反射
    ClassLoader
    简单代码// 求质数
    简单代码 //输入三个数求最大值.
    简单代码 //输入两个数加减乘除。
  • 原文地址:https://www.cnblogs.com/wy1290939507/p/4665940.html
Copyright © 2011-2022 走看看