• 链表练习


    一.单链表

    基本数据结构预留

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    #include <string>
    #include <queue>
    class Link
    {
    public:
    Link(int iata)
    {
    iData=iata;
    }
    int iData;
    Link* Next;
    void DisplayLink() {
    cout << iData << endl;
    }
    };
    class LinkList
    {
    private:
    Link* first;
    public:
    void InsertFirst(int iData)
    {
    Link* newLink=new Link(iData);
    if(IsEmpty())
    {
    first=newLink;
    first->Next=NULL;
    } else
    {
    newLink->Next=first;
    first=newLink;
    }
    }
    bool IsEmpty()
    {
    return first==NULL;
    }
    Link* GetFirstNode()
    {
    return first;
    }
    Link* GetLastNode()
    {
    Link* currentLink=first;
    while(currentLink->Next&& !IsEmpty())
    currentLink=currentLink->Next;
    return currentLink;
    }
    LinkList(){first=NULL;}
    }

    1.删除值为n的节点的直接前驱节点

    p=>r=>n变为p=>r

    思路:由于是单链表没有前驱,所以要提前记录,并且在寻找后继的过程也要做记录,若找到则将更改节点的前驱的前驱的后继(即节点前驱)

    void DeleteBefore(int iData)
    {
    //default val
    Link* p=first;
    Link* q=first->Next;
    Link* r;
    if(q!=NULL)
    r=q->Next;
    //find
    while(r!=NULL && r->iData!=iData)
    {
    p=q;
    q=r;
    r=r->Next;
    }
    //delete
    if(r!=NULL)
    {
    p->Next=q->Next;
    //p->Next=r;
    delete q;
    }
    }

    Test

    LinkList list;
    list.InsertFirst(4);
    list.InsertFirst(1);
    list.InsertFirst(2);
    list.InsertFirst(3);
    list.DeleteBefore(1);

    2.设置C={a1,b1,a2,b2…,an,bn}以hc单链表存放,将其拆分为2个链接({a1,a2…,an}{b1,b2…,bn}),可以理解为奇偶拆分

    static void Split(LinkList* ha,LinkList* hb,LinkList* hc)
    {
    //3,2,1,4=>3,2 1,4
    Link* p=hc->GetFirstNode();
    Link* ra;
    Link* rb;
    while(p!=NULL)
    {
    //add first link
    if(ha->IsEmpty())
    {
    ha->InsertFirst(p->iData);
    ra=ha->GetFirstNode();
    } else
    {
    ra->Next=p;
    ra=p;
    }
    //add second link
    p=p->Next;
    if(p!=NULL)
    {
    if(hb->IsEmpty())
    {
    hb->InsertFirst(p->iData);
    rb=hb->GetFirstNode();
    }
    else
    {
    rb->Next=p;
    rb=p;
    }
    p=p->Next;
    }
    }
    //remove end link's next
    ra->Next=rb->Next=NULL;
    }

    3.将两个有序单链表归并为一个有序单链表

    思路:与顺序表其实是一样的,前提还是有序不重复,最后的时候记得有没有合并的加一个链接,这里的效率好一些.

    static void Merge(LinkList* ha,LinkList* hb,LinkList* hc)
    {
    Link* p=ha->GetFirstNode();
    Link* q=hb->GetFirstNode();
    Link* r;
    while(p!=NULL && q!=NULL)
    {
    if(p->iData<q->iData)
    {
    if(hc->IsEmpty())
    {
    hc->InsertFirst(p->iData);
    r=hc->GetFirstNode();
    } else
    {
    r->Next=p;
    r=p;
    }
    p=p->Next;
    } else if(p->iData>q->iData)
    {
    if(hc->IsEmpty())
    {
    hc->InsertFirst(q->iData);
    r=hc->GetFirstNode();
    } else
    {
    r->Next=q;
    r=q;
    }
    q=q->Next;
    }
    }
    if(q!=NULL)
    {
    p=q;
    }
    if(p!=NULL)
    {
    hc->GetLastNode()->Next=p;
    }
    }

    4.设置单链表L为递增有序,设计一个算法删除表中值大于min或者小于max的节点

    由于是有序的,只要找出第一个大于min的节点和小于max的节点,然后连接起来就可

    因为没有前驱,还是需要一个暂存变量

    void SortOutRange(int min,int max)
    {
    //1,2,3,4,5,6=>1,2,6
    Link* start=this->GetFirstNode();
    Link* temp=start;
    while(temp!=NULL && temp->iData<min)
    {
    start=temp;
    temp=temp->Next;
    }
    Link* end=temp;
    while(end!=NULL && end->iData<=max)
    {
    end=end->Next;
    }
    start->Next=end;
    //free
    }

    5.同上,但链表改为非有序

    由于不是有序,那么就导致若每次遇到范围内则删除之,即有删除就要移位.复杂度为O(n)

    这里本应该记录被删除节点的前驱的,偷懒省去了

    void OutRange(int min,int max)
    {
    //1,3,2,5,8,6=>1,2,8,6
    Link* p;
    Link* temp=this->GetFirstNode();
    while(temp!=NULL)
    {
    p=temp;
    temp=temp->Next;
    if(p->iData>=min && p->iData<=max)
    {
    this->DeleteLink(p);
    }
    }
    //free
    }

    6.有序递增,删除值域重复的结点

    由于是有序,那么值域重复肯定是相邻的

    void SortDistinct()
    {
    //1,2,2,4,5,5,6,6=>1,2,4,5,6
    Link* p=this->GetFirstNode();
    if(p==NULL) return;
    while(p->Next!=NULL)
    {
    if(p->iData==p->Next->iData)
    {
    p->Next=p->Next->Next;
    } else
    {
    p=p->Next;
    }
    }
    }

    7.同上,非有序删除值域重复的结点(复杂度为O(n的平方)

    无序的话,就得从第1个节点与其他节点都比较一遍,每次循环则减少一次比较的次数

    比如1,2,3=>1,2 1,3=>2,3(因为1,2比较过了,即取无重复的2个数字组合)

    void Distinct()
    {
    //1,2,2,4,5,5,6,6=>1,2,4,5,6
    Link* p=this->GetFirstNode();
    Link* r;
    Link* temp;
    while(p!=NULL)
    {
    r=p->Next;
    while(r!=NULL)
    {
    temp=r;
    r=r->Next;
    if(p->iData==temp->iData)
    {
    this->DeleteLink(temp);
    }
    }
    p=p->Next;
    }
    }

    都是删除的方法,思路还是很相似的

    8.判定一个单链表是否单调递增

    很简单只要一个节点的后驱比其自身小的话就算不是递增

    bool IsIncrease()
    {
    Link* p=this->GetFirstNode();
    while(p->Next!=NULL)
    {
    if(p->iData>p->Next->iData)
    return false;
    else
    p=p->Next;
    }
    return true;
    }

    9.为单链表排序单调递增

    即冒泡排序

    10.删除单链表最小值

    即找出最小值,一遍比较一遍存,然后删除即可,关键同时还要记录其前驱

    记录一组寻常的节点和其前驱和最小值的和其前驱

    void Deletemin()
    {
    Link* pre=this->GetFirstNode();
    Link* p=pre->Next;
    Link* min=p;
    Link* minpre=pre;
    while(p!=NULL)
    {
    //min link and its pre link
    if(p->iData<min->iData)
    {
    min=p;
    minpre=pre;
    }
    //pre and current link
    pre=p;
    p=p->Next;
    }
    minpre->Next=min->Next;
    delete min;
    }

    11.倒置(reverse)

    解法1:用stack,练习用的

    void Reverse()
    {
    InsertFirst(1);
    InsertFirst(2);
    InsertFirst(3);
    //放入queue,先进后出
    queue<int> stack;
    Link* currentLink=this->GetFirstNode();
    Link* lastLink=this->GetLastNode();
    while(currentLink!=NULL)
    {
    stack.push(currentLink->iData);
    currentLink=currentLink->Next;
    }
    //3,2,1
    this->ClearList(first);
    first=NULL;
    while(!stack.empty())
    {
    int a=stack.front();
    this->InsertFirst(a);
    stack.pop();
    }
    currentLink=this->GetFirstNode();
    }

    解法2 就地倒置

    思路:
    1.因为是就地倒置,所以每个向前的元素每次都成为第一个元素
    2.然后将该元素的前驱指向原先的第一个元素

    void Reverse1()
    {
        //4,3,2,1=>1,2,3,4
        //3,4,2,1
        //2,3,4,1
        //1,2,3,4
        Link* prev=first;
        Link* r;
        first=NULL;
        while(prev!=NULL)
        {
            //r is temp store=>prev next
            r=prev->Next;
            //exchange preve and next
            prev->Next=first;
            //change firt link
            first=prev;
            //change prev link
            prev=r;
        }
    }

    方案2:

    基于上面的基础,把原链表的尾结点暂存起来,而不是每次更改首结点,等循环结束以后给首结点赋值,其他保持不变

    void Reverse2()
    {
        //4,3,2,1=>1,2,3,4
        //3,4,2,1
        //2,3,4,1
        //1,2,3,4
        Link* prev=first;
        Link* current=prev->Next;
        prev->Next=NULL;
        Link* r;
        while(current!=NULL)
        {
            //r is temp store=>prev next
            r=current->Next;
            //exchange preve and next
            current->Next=prev;
            //change firt link
            prev=current;
            //change prev link
            current=r;
        }
        first=prev;
    }

    12.两个集合的交、差、并集

    1

  • 相关阅读:
    9.8 查找表
    LeetCode——Permutations
    利用rman自己主动备份转储spfile
    linux下非root用户怎样改动root权限的文件
    做一个有主见的女生
    APP-FND-00676: 弹性域例程 FDFGDC 无法读取为此说明性弹性域指定的默认引用字段
    矩阵高速幂模板篇
    Index statistics collected bug
    位运算
    poj 1190 生日蛋糕 , 强剪枝
  • 原文地址:https://www.cnblogs.com/Clingingboy/p/1914225.html
Copyright © 2020-2023  润新知