@yudesong
2017-06-24T14:41:52.000000Z
字数 8054
阅读 566
线性表 yudesong 2017/6/24
线性表(a1,a2…,an)有唯一的第一个和最后一个元素(n≥0)。其余的有唯一的前驱和后继。 顺序表定义:用一组地址连续的存储单元依次存放的数据元素。 在顺序表的第i个位置前插入一个数据元素,需要向后移动n - i +1个元素,删除第i个位置的元素需要向前移动n- i个元素。
#include <stdio.h>#include <malloc.h>typedef char ElemType;typedef struct{ElemType data[MAX_SIZE];int len;}SqList;//初始化顺序表void initList(SqList *&L){L=(SqList *)malloc(sizeof(SqList));L->len=0;}//插入元素void insertList(SqList *&L,int i,ElemType e){i--;int j;for(j=L->len;j>i;j--)L->data[j]=L->data[j-1];L->data[i]=e;L->len++;}//删除第i个位置的元素void deleteList(SqList *&L,int i){i--;ElemType e=L->data[i];for(int j=i;j<L->len-1;j++)L->data[j]=L->data[j+1];L->len--;printf("%c",e);}//输出元素void disPlay(SqList *L){int i;for(i=0;i<;L->len;i++)printf("%c ",L->data[i]);}//释放顺序表void freeList(SqList *L){free(L);}
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.
typedef char ElemType;typedef struct Node{ElemType data;struct Node *next;}LinkList;//初始化链表void initLink(LinkList *&L){L=(LinkList *)malloc(sizeof(LinkList));L->next=NULL;}//链表插入int insertLink(LinkList *&L,int i,ElemType e){LinkList *p=L,*s;int j=0;while(j<i-1 && p!=NULL){j++;p=p->next;}if(p==NULL) return 0;else{s=(LinkList *)malloc(sizeof(LinkList));s->data=e;s->next=p->next;p->next=s;}return 1;}//删除链表中元素void deleteList(LinkList *L){LinkList *p=L,*q=p->next;while(q!=NULL){free(p);p=q;q=p->next;}}//输出链表void disPlsy(LinkList *L){LinkList *p=L->next;while(p!=NULL){printf("%c ",p->data);p=p->next;}}
// 求单链表中结点的个数unsigned int GetListLength(ListNode * pHead){if(pHead == NULL)return 0;unsigned int nLength = 0;ListNode * pCurrent = pHead;while(pCurrent != NULL){nLength++;pCurrent = pCurrent->m_pNext;}return nLength;}
// 反转单链表ListNode * ReverseList(ListNode * pHead){// 如果链表为空或只有一个结点,无需反转,直接返回原链表头指针if(pHead == NULL || pHead->m_pNext == NULL)return pHead;ListNode * pReversedHead = NULL;// 反转后的新链表头指针,初始为NULLListNode * pCurrent = pHead;while(pCurrent != NULL){ListNode * pTemp = pCurrent;pCurrent = pCurrent->m_pNext;pTemp->m_pNext = pReversedHead;// 将当前结点摘下,插入新链表的最前端pReversedHead = pTemp;}return pReversedHead;}
// 查找单链表中倒数第K个结点ListNode * RGetKthNode(ListNode * pHead, unsigned int k)// 函数名前面的R代表反向{if(k == 0 || pHead == NULL)// 这里k的计数是从1开始的,若k为0或链表为空返回NULLreturn NULL;ListNode * pAhead = pHead;ListNode * pBehind = pHead;while(k > 1 && pAhead != NULL)// 前面的指针先走到正向第k个结点{pAhead = pAhead->m_pNext;k--;}if(k > 1)// 结点个数小于k,返回NULLreturn NULL;while(pAhead->m_pNext != NULL)// 前后两个指针一起向前走,直到前面的指针指向最后一个结点{pBehind = pBehind->m_pNext;pAhead = pAhead->m_pNext;}return pBehind;// 后面的指针所指结点就是倒数第k个结点}
// 获取单链表中间结点,若链表长度为n(n>0),则返回第n/2+1个结点ListNode * GetMiddleNode(ListNode * pHead){if(pHead == NULL || pHead->m_pNext == NULL)// 链表为空或只有一个结点,返回头指针return pHead;ListNode * pAhead = pHead;ListNode * pBehind = pHead;while(pAhead->m_pNext != NULL)// 前面指针每次走两步,直到指向最后一个结点,后面指针每次走一步{pAhead = pAhead->m_pNext;pBehind = pBehind->m_pNext;if(pAhead->m_pNext != NULL)pAhead = pAhead->m_pNext;}return pBehind; // 后面的指针所指结点即为中间结点}
// 从尾到头打印链表,使用栈void RPrintList(ListNode * pHead){std::stack<ListNode *> s;ListNode * pNode = pHead;while(pNode != NULL){s.push(pNode);pNode = pNode->m_pNext;}while(!s.empty()){pNode = s.top();printf("%d\t", pNode->m_nKey);s.pop();}}//使用递归函数:// 从尾到头打印链表,使用递归void RPrintList(ListNode * pHead){if(pHead == NULL){return;}else{RPrintList(pHead->m_pNext);printf("%d\t", pHead->m_nKey);}}
// 合并两个有序链表ListNode * MergeSortedList(ListNode * pHead1, ListNode * pHead2){if(pHead1 == NULL)return pHead2;if(pHead2 == NULL)return pHead1;ListNode * pHeadMerged = NULL;if(pHead1->m_nKey < pHead2->m_nKey){pHeadMerged = pHead1;pHeadMerged->m_pNext = NULL;pHead1 = pHead1->m_pNext;}else{pHeadMerged = pHead2;pHeadMerged->m_pNext = NULL;pHead2 = pHead2->m_pNext;}ListNode * pTemp = pHeadMerged;while(pHead1 != NULL && pHead2 != NULL){if(pHead1->m_nKey < pHead2->m_nKey){pTemp->m_pNext = pHead1;pHead1 = pHead1->m_pNext;pTemp = pTemp->m_pNext;pTemp->m_pNext = NULL;}else{pTemp->m_pNext = pHead2;pHead2 = pHead2->m_pNext;pTemp = pTemp->m_pNext;pTemp->m_pNext = NULL;}}if(pHead1 != NULL)pTemp->m_pNext = pHead1;else if(pHead2 != NULL)pTemp->m_pNext = pHead2;return pHeadMerged;}//也有如下递归解法:ListNode * MergeSortedList(ListNode * pHead1, ListNode * pHead2){if(pHead1 == NULL)return pHead2;if(pHead2 == NULL)return pHead1;ListNode * pHeadMerged = NULL;if(pHead1->m_nKey < pHead2->m_nKey){pHeadMerged = pHead1;pHeadMerged->m_pNext=MergeSortedList(pHead1->m_pNext,pHead2);}else{pHeadMerged = pHead2;pHeadMerged->m_pNext=MergeSortedList(pHead1,pHead2->m_pNext);}return pHeadMerged;}
bool HasCircle(ListNode * pHead){// 快指针每次前进两步ListNode * pFast = pHead;// 慢指针每次前进一步ListNode * pSlow = pHead;while(pFast != NULL && pFast->m_pNext != NULL){pFast = pFast->m_pNext->m_pNext;pSlow = pSlow->m_pNext;if(pSlow == pFast) // 相遇,存在环return true;}return false;}
如果两个链表相交于某一节点,那么在这个相交节点之后的所有节点都是两个链表所共有的。也就是说,如果两个链表相交,那么最后一个节点肯定是共有的。先遍历第一个链表,记住最后一个节点,然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则不相交。时间复杂度为O(len1+len2),因为只需要一个额外指针保存最后一个节点地址,空间复杂度为O(1)。参考代码如下:
bool IsIntersected(ListNode * pHead1, ListNode * pHead2){if(pHead1 == NULL || pHead2 == NULL)return false;ListNode * pTail1 = pHead1;while(pTail1->m_pNext != NULL)pTail1 = pTail1->m_pNext;ListNode * pTail2 = pHead2;while(pTail2->m_pNext != NULL)pTail2 = pTail2->m_pNext;return pTail1 == pTail2;}
对第一个链表遍历,计算长度len1,同时保存最后一个节点的地址。 对第二个链表遍历,计算长度len2,同时检查最后一个节点是否和第一个链表的最后一个节点相同,若不相同,不相交,结束。 两个链表均从头节点开始,假设len1大于len2,那么将第一个链表先遍历len1-len2个节点,此时两个链表当前节点到第一个相交节点的距离就相等了,然后一起向后遍历,知道两个节点的地址相同。 时间复杂度,O(len1+len2)。参考代码如下:
ListNode* GetFirstCommonNode(ListNode * pHead1, ListNode * pHead2){if(pHead1 == NULL || pHead2 == NULL)return NULL;int len1 = 1;ListNode * pTail1 = pHead1;while(pTail1->m_pNext != NULL){pTail1 = pTail1->m_pNext;len1++;}int len2 = 1;ListNode * pTail2 = pHead2;while(pTail2->m_pNext != NULL){pTail2 = pTail2->m_pNext;len2++;}if(pTail1 != pTail2) // 不相交直接返回NULLreturn NULL;ListNode * pNode1 = pHead1;ListNode * pNode2 = pHead2;// 先对齐两个链表的当前结点,使之到尾节点的距离相等if(len1 > len2){int k = len1 - len2;while(k--)pNode1 = pNode1->m_pNext;}else{int k = len2 - len1;while(k--)pNode2 = pNode2->m_pNext;}while(pNode1 != pNode2){pNode1 = pNode1->m_pNext;pNode2 = pNode2->m_pNext;}return pNode1;}
首先判断是否存在环,若不存在结束。在环中的一个节点处断开(当然函数结束时不能破坏原链表),这样就形成了两个相交的单链表,求进入环中的第一个节点也就转换成了求两个单链表相交的第一个节点。参考代码如下:
ListNode* GetFirstNodeInCircle(ListNode * pHead){if(pHead == NULL || pHead->m_pNext == NULL)return NULL;ListNode * pFast = pHead;ListNode * pSlow = pHead;while(pFast != NULL && pFast->m_pNext != NULL){pSlow = pSlow->m_pNext;pFast = pFast->m_pNext->m_pNext;if(pSlow == pFast)break;}if(pFast == NULL || pFast->m_pNext == NULL)return NULL;// 将环中的此节点作为假设的尾节点,将它变成两个单链表相交问题ListNode * pAssumedTail = pSlow;ListNode * pHead1 = pHead;ListNode * pHead2 = pAssumedTail->m_pNext;ListNode * pNode1, * pNode2;int len1 = 1;ListNode * pNode1 = pHead1;while(pNode1 != pAssumedTail){pNode1 = pNode1->m_pNext;len1++;}int len2 = 1;ListNode * pNode2 = pHead2;while(pNode2 != pAssumedTail){pNode2 = pNode2->m_pNext;len2++;}pNode1 = pHead1;pNode2 = pHead2;// 先对齐两个链表的当前结点,使之到尾节点的距离相等if(len1 > len2){int k = len1 - len2;while(k--)pNode1 = pNode1->m_pNext;}else{int k = len2 - len1;while(k--)pNode2 = pNode2->m_pNext;}while(pNode1 != pNode2){pNode1 = pNode1->m_pNext;pNode2 = pNode2->m_pNext;}return pNode1;}
对于删除节点,我们普通的思路就是让该节点的前一个节点指向该节点的下一个节点,这种情况需要遍历找到该节点的前一个节点,时间复杂度为O(n)。对于链表,链表中的每个节点结构都是一样的,所以我们可以把该节点的下一个节点的数据复制到该节点,然后删除下一个节点即可。要注意最后一个节点的情况,这个时候只能用常见的方法来操作,先找到前一个节点,但总体的平均时间复杂度还是O(1)。参考代码如下:
void Delete(ListNode * pHead, ListNode * pToBeDeleted){if(pToBeDeleted == NULL)return;if(pToBeDeleted->m_pNext != NULL){// 将下一个节点的数据复制到本节点,然后删除下一个节点pToBeDeleted->m_nKey = pToBeDeleted->m_pNext->m_nKey;ListNode * temp = pToBeDeleted->m_pNext;pToBeDeleted->m_pNext = pToBeDeleted->m_pNext->m_pNext;delete temp;}// 要删除的是最后一个节点else{// 链表中只有一个节点的情况if(pHead == pToBeDeleted){pHead = NULL;delete pToBeDeleted;}else{ListNode * pNode = pHead;// 找到倒数第二个节点while(pNode->m_pNext != pToBeDeleted)pNode = pNode->m_pNext;pNode->m_pNext = NULL;delete pToBeDeleted;}}}
问题解答: 算法思想:首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。
bool Merge(SeqList A, SeqList B, SeqList &C){//合并有序顺序表A与B成为一个新的有序顺序表Cif(A.length+B.length>C.maxSize) //大于顺序表的最大长度return false;int i=0,j=0,k=0;while(i<A.length && j<B.length){//循环,两两比较,小者存入结果表if(A.data[i] < B.data[j])C.data[k++] = A.data[i++];elseC.data[k++]=B.data[j++];}while(i<A.length) //还剩一个没有比较完的顺序表C.data[k++] =A.data[i++];while(j<B.length)C.data[k++] = B.data[j++];C.length=k;return true;}