这篇博客记录了链表和顺序表的一些常见面试题。
包含了以下题目:
// 从尾到头打印单链表 void PrintFromTailToFront(PNode pHead); // 删除无头单链表的非尾结点,要求:不能遍历链表 void DeleteNotTailNode(PNode pos); // 在无头单链表pos位置前插入值为结点data的结点 void InsertPosFront(PNode pos, DataType data); // 用单链表模拟实现约瑟夫环 void JosephCircle(PNode* pHead, const int M); // 使用冒泡排序方法对单链表进行排序 void BubbleSort(PNode pHead); // 单链表的逆置--三个指针 void ReverseSList(PNode* pHead); // 单链表的逆置--头插法 PNode ReverseSListOP(PNode pHead); // 合并两个有序链表,合并之后依然有序 PNode MergeSList(PNode pHead1, PNode pHead2); // 查找链表的中间结点,要求只能遍历一次链表 PNode FindMiddleNode(PNode pHead); // 查找链表的倒数第K个结点,要求只能遍历链表一次 PNode FindLastKNode(PNode pHead, int K); // 删除链表的倒数第K个结点,要求只能遍历链表一次 PNode DeleteLastKNode(PNode pHead, int K); // 判断单链表是否相交?链表不带环 int IsCrossWithoutCircle(PNode pHead1, PNode pHead2); // 求不带环单链表相交交点 PNode GetCrossNode(PNode pHead1, PNode pHead2); // 判断链表是否带环 PNode IsCircle(PNode pHead); // 求环的长度 int GetCircleLen(PNode pHead); // 求环的入口点--注意推断过程 PNode GetEnterNode(PNode pHead, PNode pMeetNode); // 判断链表是否带环,链表可能带环 int IsListCrossWithCircle(PNode pHead1, PNode pHead2); //复杂链表 typedef struct CListNode { struct CListNode *next; struct CListNode *Random; DataType data; }CListNode,*PCListNode; // 复杂链表的复制 PCListNode CopyComplexList(PCListNode pHead); //求两个已排序单链表中的相同数据 void UnionSet(PNode L1, PNode L2);
实现:
#include "PSList.h" #include <stdio.h> #include <assert.h> #include <stdlib.h> //从尾到头打印单链表 void PrintFromTailToFront(PNode pHead) { if (NULL == pHead) return; PNode pCur = pHead; if (pCur) { PrintFromTailToFront(pCur->_next); printf("%d ", pCur->_data); } } // 删除链表的非尾结点,要求不能遍历链表 void DeleteNotTailNode(PNode pos) { if (NULL == pos||pos->_next==NULL) return; PNode pDelNode = pos->_next; pos->_data = pDelNode->_data; pos->_next = pDelNode->_next; free(pDelNode); } // 在链表pos位置前插入值为data的结点,不能遍历链表 void InsertPosFront(PNode pos, DataType data) { if (NULL == pos) return; PNode PNewNode = BuySListNode(pos->_data); PNewNode->_next=pos->_next; pos->_next = PNewNode; pos->_data = data; } // 约瑟夫环 void JosephCircle(PNode* pHead, const int M) { assert(pHead); PNode pCur = *pHead; if (pHead == NULL) return; while (pCur->_next != pCur) { //1.报数 int count = M; while (--count) pCur = pCur->_next; //2.删除 PNode PDelNode = pCur->_next; pCur->_data = PDelNode->_data; pCur->_next = PDelNode->_next; free(PDelNode); } *pHead = pCur; } // 使用冒泡方式对单链表进行排序 void BubbleSort(PNode pHead) { if (pHead == NULL || pHead->_next == NULL) return; int i = 0; PNode pNext = NULL; for (i = 0; i < SListSize(pHead); ++i) { int flag = 0; PNode pCur = pHead; pNext = pCur->_next; int j = 0; for (j = 0; j < SListSize(pHead)-1 - i; ++j) { if (pCur->_data > pNext->_data) { int temp = pCur->_data; pCur->_data = pNext->_data; pNext->_data = temp; flag = 1; } pCur = pCur->_next; pNext = pCur->_next; } if (flag == 0) break; } } // 单链表的逆序---三个指针 void ReverseSList(PNode* pHead) { assert(pHead); if (NULL == *pHead || NULL == (*pHead)->_next) return; PNode pPreNode = NULL; PNode pCurNode = *pHead; PNode pNextNode = NULL; while (pCurNode) { pNextNode = pCurNode->_next; pCurNode->_next = pPreNode; pPreNode = pCurNode; pCurNode = pNextNode; } *pHead = pPreNode; } // 单链表的逆序---使用头插法 PNode ReverseSListOP(PNode pHead) { if (pHead == NULL || pHead->_next == NULL) return pHead; PNode pNewHead = NULL; PNode pCurNode = pHead; while (pCurNode) { PNode pNextNode = pCurNode->_next; pCurNode->_next = pNewHead; pNewHead = pCurNode; pCurNode = pNextNode; } return pNewHead; } // 合并两个有序链表,合并起来依然要有序 PNode MergeSList(PNode pHead1, PNode pHead2) { if (NULL == pHead1 ) return pHead2; if (NULL == pHead2) return pHead1; PNode pNewHead = NULL; PNode pCur1 = pHead1; PNode pCur2 = pHead2; //确定第一个节点的值 if (pCur1->_data < pCur2->_data) { pNewHead = pCur1; pCur1 = pCur1->_next; } else { pNewHead = pCur2; pCur2 = pCur2->_next; } //向新链表中插入值 PNode pTail = pNewHead; while (pCur1&&pCur2) { if (pCur1->_data < pCur2->_data) { pTail->_next = pCur1; pCur1 = pCur1->_next; } else { pTail->_next = pCur2; pCur2 = pCur2->_next; } pTail = pTail->_next; } //若一个链表有剩余,放在新链表后面 if (pCur1) pTail->_next = pCur1; else pTail->_next = pCur2; return pNewHead; } // 查找链表的中间结点,要求只能遍历一次链表 PNode FindMiddleNode(PNode pHead) { //1->2->3->4->5 //1->2->3->4->5->6 PNode pFast = pHead; PNode pSlow = pHead; PNode pPre = pHead; while (pFast && pFast->_next) { pPre = pSlow; pFast = pFast->_next->_next; pSlow = pSlow->_next; } //求奇数个节点的中间节点前一个节点 /*if (NULL == pFast) return pPre;*/ return pSlow; } // 查找链表的倒数第K个结点 PNode FindLastKNode(PNode pHead, int K) { if (NULL == pHead || K <= 0) return NULL; PNode pFast = pHead; PNode pSlow = pHead; int count = K; //快的指针先走K步 while (count--) { if (NULL == pFast) return NULL; pFast = pFast->_next; } //然后快慢指针一起走,慢指针指向的就是倒数第K个节点 while (pFast) { pFast = pFast->_next; pSlow = pSlow->_next; } return pSlow; } // 删除链表的倒数第K个结点,要求只能遍历链表一次 PNode DeleteLastKNode(PNode pHead, int K) { if (NULL == pHead || K <= 0) return NULL; PNode pFast = pHead; PNode pSlow = pHead; PNode pPre = NULL; int count = K; //快的指针先走K步 while (count--) { if (NULL == pFast) return NULL; pFast = pFast->_next; } //然后快慢指针一起走,慢指针指向的就是倒数第K个节点 while (pFast) { pPre = pSlow; pFast = pFast->_next; pSlow = pSlow->_next; } //删除 pPre->_next = pSlow->_next; free(pSlow); return pHead; } // 判断两个单链表是否相交---链表不带环 int IsCrossWithoutCircle(PNode pHead1, PNode pHead2) { if (NULL == pHead1 || NULL == pHead2) return 0; PNode pTail1 = pHead1; PNode pTail2 = pHead2; //找链表1和链表2的尾节点 while (pTail1) pTail1 = pTail1->_next; while (pTail2) pTail2 = pTail2->_next; //若尾节点相同,则俩链表相交 return pTail1 == pTail2; } // 如果相交 获取交点 PNode GetCrossNode(PNode pHead1, PNode pHead2) { int size1 = 0; int size2 = 0; PNode pCur1 = pHead1; PNode pCur2 = pHead2; if (!IsCrossWithoutCircle(pHead1, pHead2)) return NULL; //两个链表相差的长度 while (pCur1) { size1++; pCur1 = pCur1->_next; } while (pCur2) { size2++; pCur2 = pCur2->_next; } int gap = size1 - size2; pCur1 = pHead1; pCur2 = pHead2; //让长链表先走差值步 if (gap > 0) { while (gap--) pCur1 = pCur1->_next; } else { while (gap++) pCur2 = pCur2->_next; } //然后两个一起走直到相等,即为交点 while (pCur1 != pCur2) { pCur1 = pCur1->_next; pCur2 = pCur2->_next; } return pCur1; } //链表是否带环 PNode IsCircle(PNode pHead) { PNode pFast = pHead; PNode pSlow = pHead; while (pFast&&pFast->_next) { pFast = pFast->_next->_next; pSlow = pSlow->_next; if (pFast == pSlow) return pFast; } return NULL; } //求环的长度 int GetCircleLen(PNode pHead) { PNode pMeetNode = IsCircle(pHead); PNode pCur = pMeetNode->_next; int size = 1; if (NULL == pMeetNode) return 0; while (pCur != pMeetNode) { pCur = pCur->_next; size++; } return size; } //求环的入口点 PNode GetEnterNode(PNode pHead, PNode pMeetNode) { if (NULL == pHead || NULL == pMeetNode) return NULL; PNode pH = pHead; PNode pM = pMeetNode; while (pH != pM) { pH = pH->_next; pM = pM->_next; } return pH; } // 判断链表是否相交,链表可能带环 int IsListCrossWithCircle(PNode pHead1, PNode pHead2) { if (NULL == pHead1 || NULL == pHead2) return 0; //1.判断两个链表是否带环 PNode pMeetNode1 = IsCircle(pHead1); PNode pMeetNode2 = IsCircle(pHead2); //2.两个链表都不带环 //求两个链表的最后一个元素,若最后一个元素相等则相交 if (NULL == pMeetNode1&&NULL == pMeetNode2) { PNode pTail1 = pHead1; while (pTail1->_next) pTail1 = pTail1->_next; PNode pTail2 = pHead2; while (pTail2->_next) pTail2 = pTail2->_next; if (pTail1 == pTail2) return 1; } //3.两个链表都带环 else if (pMeetNode1&&pMeetNode2) { PNode pCur = pMeetNode1; do { if (pCur == pMeetNode2) return 2; pCur = pCur->_next; } while (pCur != pMeetNode1); } return 0; } // 复杂链表的复制 PCListNode CopyComplexList(PCListNode pHead) { //1.向链表的每个元素后面插入值相同的元素 PCListNode pOldNode = pHead; PCListNode pNewNode = NULL; while (pOldNode) { pNewNode = (PCListNode)malloc(sizeof(CListNode)); pNewNode->next = NULL; pNewNode->Random = NULL; pNewNode->data = pOldNode->data; if (NULL == pNewNode) return NULL; pNewNode->next = pOldNode->next; pOldNode->next = pNewNode; pOldNode = pNewNode->next; } //2.改变链表的随机域 pOldNode = pHead; while (pOldNode) { pNewNode = pOldNode->next; if (pOldNode->Random == NULL) pNewNode->Random = NULL; else pNewNode->Random = pOldNode->Random->next; pOldNode = pNewNode->next; } //3.将新链表卸下来 pOldNode = pHead; PCListNode pNewHead = pOldNode->next; while (pOldNode->next) { pNewNode = pOldNode->next; pOldNode->next = pNewNode->next; pOldNode = pNewNode; } return pNewHead; } //求两个已排序单链表中的数据 void UnionSet(PNode L1, PNode L2) { if (NULL == L1 || NULL == L2) return; int data = 0; while (L1&&L2) { if (L1->_data == L2->_data) { if(L1->_data!=data) printf("%d ", L1->_data); data = L1->_data; L1 = L1->_next; } else if (L1->_data < L2->_data) L1 = L1->_next; else L2 = L2->_next; } }
顺序表和链表的比较
(1)顺序表支持随机访问,单链表不支持随机访问
(2)顺序表插入/删除数据效率很低,时间复杂度为o(N),除尾插尾删,
单链表插入/删除效率高,时间复杂度为O(1),容易造成内存碎片
(3)顺序表的CPU高速缓存效率更高,单链表CPU高速缓存效率低