表,栈和队列

来源:互联网 发布:淘宝盗图技巧 编辑:程序博客网 时间:2024/06/05 19:17

表,栈和队列

表的简单数组

对表的所有操作都可以使用数组来实现。虽然数组是静态分配的,但是内部存储数组的vector类允许在需要时的时候将数组的大小增加一倍。这解决了使用数组是需要对表的大小的最大值进行估计的问题。

数组实现使得printList以线性时间运行,而findKth则花费常数时间。然而,插入和删除的花费有可能是昂贵的,这取决与插入和删除发生的位置。在最坏的情况下,在位置0(在表的最前面)插入需要将整个数组后移一个位置以空出空间来;则删除第一个元素则需要将表中的所有元素前一一个位置,因此这两种操作的最坏情况为O(n)。平均来看,这两种运算操作都需要移动表的一半的元素,因此仍需要线性时间。另一方面,如果所有的操作都发生在表的末尾,就不需要移动任何元素,那么添加和删除的操作都将花费O(1)时间。

简单链表

为了避免插入和删除的线性开销,需要允许表的不连续存储,否则表的部分或全部就需要整体移动。链表由一系列不必存在内存中的相连的节点组成。每一个结点均含有表元素和到包含该元素后继元的结点的链(link)。我们称之为next链。最后一个单元的next链指向NULL

为了执行printList()find(x),我们只需要从表的第一个结点开始然后用next链遍历该表即可。与数组实现一样,这种操作显然是花费线性时间的,但是这个常数可能比用数组实现时要大。findKth操作不如数组实现时的效率高;findKth(i)花费O(i)的时间并以明显的方式遍历链表完成。在实践中这个界是保守的,因为调用findKth常常是以(按i)排序的方式进行。

链表的头插法和尾插法

头插法:在头结点(为了操作方便,在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以存储数据标题、表长等信息,也可以不存储任何信息,其指针域存储第一个结点的首地址)。Head之后插入数据,其特点是读入的数据顺序与线性表的逻辑顺序正相反。

p->next=head->next;head->next=p;

尾插法:将每次插入的新结点放在链表的尾部

s->next=r->next;r->next=s;r=s;

链表题目

1.给定两个链表,分别表示两个非负整数。它们的数字逆序存储在链表中,且每个结点只存储一个数字,计算两个数的和,并且返回和的链表头指针。

如:输入:243564,输出708

typedef  struct tagSNode{    int value;    tagSNode* pNext;    tagSNode(int v):value(v),pNext(NULL){}}SNode;SNode* Add(SNode* pHead1,SNode* pHead2){    SNode* pSum=new SNode(0);    SNode* pTail=pSum;  //新结点插入到pTail的后面    SNode* p1=pHead1->pNext;    SNode* p2=pHead2->pNext;    SNode* pCur;    int carry=0; //进位    int value;    while(p1 && p2) {        value = p1->value + p2->value + carry;        carry = value / 10;        pCur = new SNode(value);        value %=10;        pTail->pNext = pCur; //新结点链接到pTail的后面        pTail = pCur;        p1 = p1->pNext; //处理下一位        p2 = p2->pNext;    }    //处理较长的链    SNode* p=p1?p1:p2;    while(p)    {        value=p->value+carry;        carry=value/10;        value %=10;        pCur=new SNode(value);        pTail->pNext=pCur;        pTail=pCur;        p=p->pNext;    }    //处理可能存在的进位    if(carry!=0)        pTail->pNext=new SNode(carry);    return pSum;}

2.链表的部分原地翻转:给定一个链表,翻转该链表从mn的位置。要求直接翻转而非直接申请空间。假定给出的参数满足1<=n<=m<=链表的长度。

typedef  struct tagSNode{    int value;    tagSNode* pNext;    tagSNode(int v):value(v),pNext(NULL){}}SNode;void Reverse(SNode* pHead,int from,int to){    SNode* pCur=pHead->pNext;    int i;    for(i=0;i<from-1;i++){ //找到第m-1个结点,即开始翻转的第一个结点的前驱,记做head;        pHead=pCur;        pCur=pCur->pNext;    }    SNode* pPre=pCur;    pCur=pCur->pNext;    to--;    SNode* pNext;    for(;i<to;i++) {  //以head为起始结点遍历n-m次,将第i次时,将找到的结点插入到head的next中即可        pNext = pCur->pNext;        pCur->pNext = pHead->pNext;        pHead->pNext = pCur; //头插法        pPre->pNext = pNext;        pCur = pNext;    }    }

3.(1)给定排序的链表,删除重复元素,只保留重复元素第一次出现的结点:若p>next的值和p的值相等,则将p>next>next赋值给p,删除p>next;重复上述过程,直至链表尾端。

(2)若发现重复元素则重复元素全部删除。

typedef  struct tagSNode {    int value;    tagSNode* pNext;    tagSNode(int v):value(v),pNext(NULL){}   }SNode;void DeleteDuplicateNode(SNode* pHead){    SNode* pPre=pHead->pNext;    SNode* pCur;    while(pPre){        pCur=pPre->pNext;        if(pCur &&(pCur->value==pPre->value))        {            pPre->pNext=pCur->pNext;            delete pCur;        }else{            pPre=pCur;        }    }}void DeleteDuplicatesNode2(SNode* pHead ){    SNode* pPre=pHead;    SNode* pCur=pPre->pNext;    SNode* pNext;    while(pCur){        pNext=pCur->pNext;        while(pNext&&(pCur->value==pNext->value))        {            pPre->pNext=pNext;            delete pCur;            pCur=pNext;            pNext=pCur->pNext;        }        pPre=pCur;        pCur=pNext;    }}void DeleteDuplicatesNode3(SNode* pHead ) {    SNode *pPre = pHead;    SNode *pCur = pPre->pNext;    SNode *pNext;    bool bDup;    while (pCur) {        pNext = pCur->pNext;        bDup = false;        while (pNext && (pCur->value == pNext->value)) {            pPre->pNext = pNext;            delete pCur;            pCur = pNext;            pNext = pCur->pNext;            bDup = true;        }        if (bDup)   //此刻的pCur与原数据重复,删之        {            pPre->pNext = pNext;            delete pCur;        } else {            pPre = pCur;        }        pCur = pNext;    }}

4.给定一个链表和一个值x,将链表划分为两部分,是得划分后小于x的结点在前,大于x的结点在后。这两部分要保持原链表中的出现顺序。

分别申请两个指针p1p2,小于x的添加到p1中,大于等于x的添加到p2中;最后,将p2链接到p1的末端即可,时间复杂度是O(n),空间复杂度为O(1);该问题可以说明:快速排序对于单链表存储结构仍然适用,但不是所有排序都方便使用链表存储。

void Partition(SNode* pHead, int pivotKey){    //两个链表的头指针    SNode* pLeftHead=new SNode(0);    SNode* pRightHead=new SNode(0);    //两个链表的当前最后一个元素    SNode* left=pLeftHead;    SNode* right=pRightHead;    SNode* p=pHead->pNext;    while(p)  //遍历原链表    {        if(p->value<pivotKey)        {            left->pNext=p;            left=p;        }        else        {            right->pNext=p;            right=p;        }        p=p->pNext;    }    //将right链接到left尾部    left->pNext=pRightHead->pNext;    right->pNext=NULL;    //将整理好的链表赋值给当前链表头部    pHead->pNext=pLeftHead->pNext;    delete pLeftHead;    delete pRightHead;}

5.单链公共结点问题,令两链表的长度为mn,不妨设m>=n,由于两个链表从第一个公共结点到链表的尾结点是完全重合的。所以前面的(mn)个结点一定没有公共结点。算法:先分别遍历两个链表得到它们的长度mn。同步遍历两链表,直到周到相同结点或到链表结束,时间复杂度为O(m+n)

typedef  struct tagSNode{    int value;    tagSNode* pNext;    tagSNode(int v):value(v),pNext(NULL){}}SNode;int CalcLength(SNode* p){    int nLen=0;    while(p)    {        p=p->pNext;        nLen++;    }    return nLen;}SNode* FindFirstSameNode(SNode* pA,SNode* pB){    //因为有头指针,所以指向第一个有效结点    pA=pA->pNext;    pB=pB->pNext;    //计算两个链表的长度    int nA=CalcLength(pA);    int nB=CalcLength(pB);    if(nA>nB)    {        swap(pA,pB);        swap(nA,nB);    }    for(int i=0;i<nB-nA;i++)    {        if(pA==pB)            return pA;        pA=pA->pNext;        pB=pB->pNext;    }    return NULL;}