单链表
线性表:具有相同数据类型的n个数据元素的有限序列
单链表:线性表的链式存储,通过一组任意的存储单元来存储线性表中的元素。
链表结点:包含数据域存放数据元素,next指针域存放后继结点的地址
类型描述如下:
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; // 指针域
}LNode, *LinkList;
优点:解决顺序表需要大量连续存储空间的缺点
缺点:指针域会浪费存储空间
不需要使用地址连续的存储单元
链表算法: 1 给链表加入一个头结点往往能简化问题 2 插入、删除链表中的元素时,需要注意可能需要事先将链表当前需要处理结点的前一个结点的保存下来 3 处理链表问题,关键就是处理好结点之间的指向关系 |
如何合并两个有序链表
已知两个链表head1和head2各自有序(例如升序排序),请把它们合并成一个链表,
要求合并后的链表仍然有序。
分析
情况1:
curr1.data > curr2.data,则
说明curr2应该插入到curr1结点的前一个结点的位置,则执行:
curr2.nextNode = curr1
prev1.nextNode = curr2
注意: 需要将prev1设置为新插入的curr2,否则出错
prev1 = curr2
并且curr2应该继续向下走
curr2 = next2
next2 = next2.nextNode if next2 else None
情况2:
curr1.data <= curr2.data, 则
prev1 = curr1
curr1 = next1
next1 = next1.nextNode if next1 else None
说明curr1需要继续遍历,直到curr1.data大于curr2.data,
然后重复情况1的步骤
如果最后curr2不为空,则
prev1.nextNode = curr2
代码如下
def mergeList(head1, head2): if not head1 and not head2: return elif not head1: return head2 elif not head2: return head1 prev1 = head1 curr1 = head1.nextNode next1 = curr1.nextNode if curr1 else None prev2 = head2 curr2 = head2.nextNode next2 = curr2.nextNode if curr2 else None while curr1 and curr2: if curr1.data > curr2.data: curr2.nextNode = curr1 prev1.nextNode = curr2 # 注意,这里prev1也要更新为curr2 prev1 = curr2 prev2 = curr2 curr2 = next2 next2 = next2.nextNode if next2 else None else: prev1 = curr1 curr1 = next1 next1 = next1.nextNode if next1 else None # 链表2不为空,链表1为空,说明链表2剩余结点的值都大于链表1的最后一个结点 # 链表1需要接上链表2 if curr2: prev1.nextNode = curr2 return head1 |
类别-编号 |
题目 |
来源 |
1 |
从尾到头打印链表 输入一个链表,从尾到头打印链表每个节点的值。 输入: 每个输入文件仅包含一组测试样例。 每一组测试案例包含多行,每行一个大于0的整数,代表一个链表的节点。第一行是链表第一个节点的值,依次类推。当输入到-1时代表链表输入完毕。-1本身不属于链表。 输出: 对应每个测试案例,以从尾到头的顺序输出链表每个节点的值,每个值占一行。 样例输入: 1 2 3 4 5 -1 样例输出: 5 4 3 2 1 |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39092187 关键: 逆序输出,这得采用头插法 1 典型的后进先出,可以在遍历的同时,加上一个栈表示。关键:如何建立单链表,可以采用头插法
代码: typedef struct Node { int _iValue; struct Node* _next; }Node;
void reversePrint(Node* pHead,stack { while(!stackNode.empty()) { Node* pNode = stackNode.top(); stackNode.pop(); printf("%d\n",pNode->_iValue); } }
void freeList(Node* pHead) { Node* pDel; while(pHead != NULL) { pDel = pHead; pHead = pHead->_next; delete pDel; pDel = NULL; } }
void process() { int x; bool isFirst = true; Node* pHead,*pCur,*pNew; stack while(EOF != scanf("%d",&x))//采用尾插法建立单链表,这是逆序 { if(x == -1) { break; } if(!isFirst)//建立其余节点 { pNew = new Node(); pNew->_iValue = x; pNew->_next = NULL; stackNode.push(pNew); pCur->_next = pNew; pCur = pNew; } else//建立头节点 { pHead = new Node(); pHead->_iValue = x; pHead->_next = NULL; stackNode.push(pHead); pCur = pHead; isFirst = false; } } //尾插法是顺序,下面通过栈来逆序打印 reversePrint(pHead, stackNode); freeList(pHead);//记住,一定要释放内存 } |
2 |
在O(1)时间删除链表节点: 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该节点。链表结点与函数的定义如下 |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138121 关键: 找到该节点{如果该节点后面还有节点,将其值与其后面的节点交换,然后删除该节点本身 {如果该节点后面没有节点,需要找到其前面的节点,将其前面节点的指向节点指针置空 如果该节点为头节点,那么另其头节点为其后面节点的指针 如果只有一个节点,删除的是头尾节点,需要把链表的头节点置为空
关键: 1 if(pDelNode->_next != NULL)//考虑删除的节点不是尾节点,只需要看待删除节点的指向是否为空即可 2 else if(pDelNode == *pHead)//考虑链表只有一个节点,即头节点指针和待删除节点指针相同即可 { delete pDelNode;//直接删除即可 pDelNode = *pHead = NULL; } 3else//考虑有多个节点,但删除的节点是尾节点的情形 delete pDelNode; pDelNode = NULL;//一定要注意,是删除的指针置空,否则变成野指针 4 必须确保要删除的节点在链表中
代码: typedef struct Node { int _iVal; struct Node* _next; }Node;
void delNod(Node** pHead,Node* pDelNode)//pHead是指向链表指针的指针,是二级指针 { if(!pHead || !pDelNode)//鲁棒性 { return ; } if(pDelNode->_next != NULL)//考虑删除的节点不是尾节点,只需要看待删除节点的指向是否为空即可 { Node* pNextNode = pDelNode->_next; pDelNode->_iVal = pNextNode->_iVal;//重新设置值 pDelNode->_next = pNextNode->_next; delete pNextNode; pNextNode = NULL; } else if(pDelNode == *pHead)//考虑链表只有一个节点,即头节点指针和待删除节点指针相同即可 { delete pDelNode;//直接删除即可 pDelNode = *pHead = NULL; } else//考虑有多个节点,但删除的节点是尾节点的情形 { Node* pNode = *pHead; while(pNode->_next != pDelNode) { pNode = pNode->_next; } pNode->_next = NULL; delete pDelNode; pDelNode = NULL;//一定要注意,是删除的指针置空,否则变成野指针 } } |
3 |
链表中倒数第k个节点: 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第一个节点。例如一个链表有6个节点,从头节点开始它们的值依次是1,2,3,4,5,6。这个链表的倒数第三个节点是值为4的结点。
输入: 输入可能包含多个测试样例,输入以EOF结束。 对于每个测试案例,输入的第一行为两个整数n和k(0<=n<=1000, 0<=k<=1000):n代表将要输入的链表元素的个数,k代表要查询倒数第几个的元素。 输入的第二行包括n个数t(1<=t<=1000000):代表链表中的元素。 输出: 对应每个测试案例, 若有结果,输出相应的查找结果。否则,输出NULL。 样例输入: 5 2 1 2 3 4 5 1 0 5 样例输出: 4 NULL |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138143 与题目12是同一个题目,以第12题的答案为准
|
4 |
反转链表 定义一个函数,输入一个链表的头结点,反转该链表并输出翻转后链表的头结点。
输入: 输入可能包含多个测试样例,输入以EOF结束。 对于每个测试案例,输入的第一行为一个整数n(0<=n<=1000):代表将要输入的链表的个数。 输入的第二行包含n个整数t(0<=t<=1000000):代表链表元素。 输出: 对应每个测试案例, 以此输出链表反转后的元素,如没有元素则输出NULL。 样例输入: 5 1 2 3 4 5 0 样例输出: 5 4 3 2 1 NULL |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138155 关键: 1 if(pHead == NULL || *pHead == NULL)//如果指向链表指针的指针为空或者为空链表,均返回false { return NULL; } 2 Node* pCurNode,*pFrontNode = NULL,*pNextNode,*pReverseHead;//初始时要设置上一个节点为空,这是因为原来的首节点的指向要为空,这样才能变成最后一个 3 while(pCurNode != NULL)//一旦为空,表示到结尾了,该结束了 { pNextNode = pCurNode->_next;//保存下一个节点 if(pNextNode == NULL)//判断当前节点是否就是尾节点 { pReverseHead = pCurNode; } pCurNode->_next = pFrontNode;//让当前节点指向前一个节点 pFrontNode = pCurNode;//继续向下遍历 pCurNode = pNextNode; 4 if(pHead == NULL || *pHead == NULL)//有关链表的任何操作需要判定指针是否为空
代码: Node* reverseList(Node** pHead) { if(pHead == NULL || *pHead == NULL)//如果指向链表指针的指针为空或者为空链表,均返回false { return NULL; } bool isFirst = true; //初始时要设置上一个节点为空,这是因为原来的首节点的指向要为空,这样才能变成最后一个 Node* pCurNode,*pFrontNode = NULL,*pNextNode,*pReverseHead; pCurNode = *pHead;//就从头结点开始循环,避免后面节点的判断 while(pCurNode != NULL)//一旦为空,表示到结尾了,该结束了 { pNextNode = pCurNode->_next;//保存下一个节点 if(pNextNode == NULL)//判断当前节点是否就是尾节点 { pReverseHead = pCurNode; } pCurNode->_next = pFrontNode;//让当前节点指向前一个节点 pFrontNode = pCurNode;//继续向下遍历 pCurNode = pNextNode; } return pReverseHead; } |
5 |
合并两个排序的链表 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是按照递增排序的。例如输入图中的链表1和链表2,则合并之后的升序链表入链表3所示 |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138171 参见经典例题解析 |
6 |
复杂链表的复制: 请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个结点除了有一个m_pNext指针 指向下一个结点外,还有一个m_pSibling指向链表中的任意结点或者NULL。 A->B->C->D->E 兄弟指向" A指向C B指向E D指向B E,C指向NUL
输入: 输入可能包含多个测试样例,输入以EOF结束。 对于每个测试案例,输入的第一行为一个整数n (1<=n<=1000):n代表将要输入的链表元素的个数。(节点编号从1开始)。 接下来有n个数,表示链表节点中的值。 接下来有n个数Ti,Ti表示第i个节点的另一个指针指向。 Ti = 0 表示这个指针为NULL。 输出: 对应每个测试案例, 输出n行,每行有二个数,第一个代表当前节点值,第二个代表当前节点的特殊指针的值。 样例输入: 5 1 2 3 4 5 3 5 0 2 0 样例输出: 1 3 2 5 3 0 4 2 5 0 |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138361 方法3: 1:根据原始节点N创建对应的结点N',并把N'链接在N的后面,形成如下: A->A'->B->B'->C->C'->D->D'->E->E' 2:设置复制出来的节点的m_pSibling。假设原始链表上的N的m_pSibling指向结点S,复制出来的N'是N的m_pNext 指向的节点。S'也是S的m_pNext指向的结点。 A->A'->B->B'->C->C'->D->D'->E->E' 兄弟指向: A指向C A'指向C' B指向E B'指向E' D指向B D'指向B' 3:把长链表拆分成两个链表:把奇数位置的节点用m_pNext链接起来就是原始链表,把偶数位置的节点用m_pNext链接 起来就是复制出来的链表。
关键: 1 pNewNode = createNode();//创建一个新节点 pNewNode->_iVal = pNode->_iVal;//注意克隆,就不需要用到原来的数组,复制的时候,每次复制,将N'插入在N后面, pNewNode->_next = pNode->_next; pNewNode->_sibling = NULL; 2 if(pNode->_sibling)//如果原来节点N的兄弟节点不为空 { pCloneNode->_sibling = pNode->_sibling->_next;//经典,S = S'->next = N->_sibling->_next 3 pCloneNode->_next = pNode->_next;//更新克隆节点的指向 pCloneNode = pCloneNode->_next; pNode->_next = pCloneNode->_next;//重新拆分链表为两个链表,偶数的作为复制链表 pNode = pNode->_next; 4 1:根据原始节点N创建对应的结点N',并把N'链接在N的后面,形成如下: A->A'->B->B'->C->C'->D->D'->E->E' 2:设置复制出来的节点的m_pSibling。假设原始链表上的N的m_pSibling指向结点S,复制出来的N'是N的m_pNext 指向的节点。S'也是S的m_pNext指向的结点。 A->A'->B->B'->C->C'->D->D'->E->E' 兄弟指向: A指向C A'指向C' B指向E B'指向E' D指向B D'指向B' 3:把长链表拆分成两个链表:把奇数位置的节点用m_pNext链接起来就是原始链表,把偶数位置的节点用m_pNext链接 起来就是复制出来的链表。
代码:
const int MAXSIZE = 10000;
typedef struct Node { int _iVal; int _iCode;//结点编号 Node* _next; Node* _sibling; }Node; Node nodeArr[MAXSIZE]; int _iIndex; Node* createNode() { ++_iIndex; nodeArr[_iIndex]._next = nodeArr[_iIndex]._sibling = NULL; nodeArr[_iIndex]._iCode = _iIndex; return &nodeArr[_iIndex]; }
void cloneList(Node** pHead) { if(!pHead || !(*pHead)) { return; } Node* pNode = *pHead; Node* pNewNode; while(pNode)// { pNewNode = createNode();//创建一个新节点 pNewNode->_iVal = pNode->_iVal;//注意克隆,就不需要用到原来的数组,复制的时候,每次复制,将N'插入在N后面, pNewNode->_next = pNode->_next; pNewNode->_sibling = NULL; pNode->_next = pNewNode; pNode = pNewNode->_next; } }
void connectSibling(Node** pHead) { if(!pHead || !(*pHead)) { return; } Node* pNode = *pHead,*pCloneNode; while(pNode) { pCloneNode = pNode->_next; if(pNode->_sibling)//如果原来节点N的兄弟节点不为空 { pCloneNode->_sibling = pNode->_sibling->_next;//经典,S = S'->next = N->_sibling->_next } pNode = pCloneNode->_next; } }
Node* reconnectNode(Node** pHead) { if(!pHead || !(*pHead)) { return NULL; } Node* pNode = *pHead; Node* pCloneHead = NULL; Node* pCloneNode = NULL; if(pNode != NULL) { pCloneHead = pCloneNode = pNode->_next; pNode->_next = pCloneNode->_next; pNode = pNode->_next; } while(pNode) { pCloneNode->_next = pNode->_next;//更新克隆节点的指向 pCloneNode = pCloneNode->_next; pNode->_next = pCloneNode->_next;//重新拆分链表为两个链表,偶数的作为复制链表 pNode = pNode->_next; } return pCloneHead; } |
7 |
二叉搜索树与双向链表: 输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中 节点指针的指向。比如,输入图中左边的二叉搜索树,则输出转换之后的排序双向链表。 10 6 14 4 812 16 转换为: 4->6->8->10->12->14->16 <- <- <- <- <- <- |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39138375 输入: 输入可能包含多个测试样例。 对于每个测试案例,输入的第一行为一个数n(0 接下来的n行,每行为一个二叉搜索树的先序遍历序列,其中左右子树若为空则用0代替。 输出: 对应每个测试案例, 输出将二叉搜索树转换成排序的双向链表后,从链表头至链表尾的遍历结果。 样例输入: 1 2 1 0 0 3 0 0 样例输出: 1 2 3 关键: 1 if(i != s2)//如果存在左子树,i = s2,说明第一个节点就是根节点,那么中序后面的全是右子树,没有左子树, //注意这里因该是i!=s2 2 while(pHeadList && pHeadList->_left)//如果头节点不空,且左孩子不空,即双向链表的前一个结点不空 3 if(pCurNode->_left)//左子树不空,递归调用左子树 { convertNode(pCurNode->_left,pLastNode); } pCurNode->_left = *pLastNode;//根节点指向双向链表最后一个节点 if(*pLastNode)//如果双向链表最后一个节点不空,指向根节点。(若为空,没必要指向) { (*pLastNode)->_right = pCurNode; } *pLastNode = pCurNode;//使根节点成为双向链表最后的末尾节点,为下面用根节点链接右链表的最小节点做准备 if(pCurNode->_right) { convertNode(pCurNode->_right,pLastNode); } 代码: void convertNode(Node* pHead,Node** pLastNode) { if(!pHead) { return; } Node* pCurNode = pHead; if(pCurNode->_left)//左子树不空,递归调用左子树 { convertNode(pCurNode->_left,pLastNode); } pCurNode->_left = *pLastNode;//根节点指向双向链表最后一个节点 if(*pLastNode)//如果双向链表最后一个节点不空,指向根节点。(若为空,没必要指向) { (*pLastNode)->_right = pCurNode; } *pLastNode = pCurNode;//使根节点成为双向链表最后的末尾节点,为下面用根节点链接右链表的最小节点做准备 if(pCurNode->_right) { convertNode(pCurNode->_right,pLastNode); } } Node* convert(Node* pHead) { Node* pLastNode = NULL; convertNode(pHead,&pLastNode); Node* pHeadList = pLastNode; //如果头节点不空,且左孩子不空,即双向链表的前一个结点不空 while(pHeadList && pHeadList->_left) { pHeadList = pHeadList->_left; } return pHeadList; } |
8 |
两个链表的第一个公共结点: 输入两个链表,找出它们的第一个公共结点。 输入: 输入可能包含多个测试样例。 对于每个测试案例,输入的第一行为两个整数m和n(1<=m,n<=1000):代表将要输入的两个链表的元素的个数。 接下来的两行,第一行为第一个链表的所有元素,中间用空格隔开。第二行为第二个链表的所有元素,中间用空格隔开。 输出: 对应每个测试案例, 输出两个链表的第一个公共结点的值。 如果两个链表没有公共结点,则输出“My God”。 样例输入: 5 4 1 2 3 6 7 4 5 6 7 3 3 1 5 7 2 4 7 2 3 1 3 4 5 6 样例输出: 6 7 My God |
剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/39187861 分析: 这是数据结构的一道题目。 设长链表长度为L=5, 短链表长度为S = 3, 那么先让长链表走L-S= 2步, 那么长链表从第3个节点开始,短链表从第一个节点开始,依次比较即可。
代码: int firstCommonNode(Node* pHead1,int iLen1,Node* pHead2,int iLen2) { if(!pHead1 || !pHead2) { return INF; } Node* pLongList,*pShortList; if(iLen1 > iLen2)//如果链表1是长链表 { pLongList = pHead1; pShortList = pHead2; } else { pLongList = pHead2; pShortList = pHead1; } int maxLen = iLen1 > iLen2 ? iLen1 : iLen2; int minLen = iLen1 + iLen2 - maxLen; int diffLen = maxLen - minLen; while(diffLen-- && pLongList)//接下来,让长链表先多diffLen步 { pLongList = pLongList->_next; } while(pLongList && pShortList) { if(pLongList->_iVal == pShortList->_iVal) { return pLongList->_iVal; } else { pLongList = pLongList->_next; pShortList = pShortList->_next; } } return 1000000000; } |
9 |
从无头单链表中删除节点 假设有一个没有头指针的单链表。一个指针指向此单链表中间的某一个节点(不是第一个,也不是最后一个),请将该节点从单链表中删除 |
编程之美 https://blog.csdn.net/qingyuanluofeng/article/details/47187597 参见:剑指offer在O(1)时间删除节点,本质上是将后面节点的节点值赋给待删除节点,然后删除后面的节点即可 参见题目2 |
10 |
编程判断两个链表是否交叉:(本质判断两个链表是否有公共结点) 给出两个单向链表的头指针,如h1,h2,判断这两个链表是相交。假设两个链表均不带环。 h1->->->
h2->->-> |
编程之美 https://blog.csdn.net/qingyuanluofeng/article/details/47187635 这道题目: 参见剑指,王道数据结构 让长链表先走iLongLen - iShortLen步之后,两个链表一起走 因为两个链表相交,这两个链表会有共同的节点,而节点地址是相同的 解法三: 由于两个链表都没有换,可以把第二个链表接在第一个链表后面。如果得到的链表有环,则说明这两个链表相交。 解法四: 如果相交,那么两个链表的最后一个节点一定是共有的 先遍历第一个链表,记住最后一个节点,然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,那么相交, 时间复杂度为O(Length(h1) + Length(h2)) |
11 |
移除未排序链表中重复的节点 编写代码,移除未排序链表中的重复节点
输入: 5(元素个数) 1 3 1 2 3 输出: 1 2 3 |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53780547 书上解法:在链表建立好后,遍历链表,如果当前元素没有在散列表中出现, 则将当前元素加入散列表;否则,说明当前元素重复,删除该节点 时间复杂度为O(N),我使用的方法时间复杂度相同,但是优先删除前面 重复的元素,书上的解法优先删除后面重复的元素 关键: 1 判断当前节点的值是否在散列表出现,没有出现则加入散列表;否则删除当前节点
代码:
//构建连边,按照尾插法来做,返回节点值到出现次数的映射 map { map if(pArray == NULL) { return valueToCount; } if(head == NULL) { return valueToCount; }
//尾插法: 保留最后一个结尾节点,将新生成的节点插入在结尾节点,并令结尾节点为当前节点 Node* pLast = head; //int num = sizeof(pArray) / sizeof(int); for(int i = 0 ; i < num ; i++) { int value = *(pArray + i); Node* pNode = new Node(); pNode->value = value; pLast->pNext = pNode; pLast = pNode;
//统计<结点值,出现次数的映射> map //如果找到结点值,累加出现次数 if(it != valueToCount.end() ) { it->second += 1; } else { valueToCount.insert(pair } } return valueToCount; }
//删除重复节点 void deleteRepeatedNodes(map { if(pHead == NULL) { return; } if( valueToCount.empty() ) { return; } Node* pNode = pHead->pNext; Node* pPrevious = pHead;
//如果当前节点非空,开始统计 while(pNode) { int value = pNode->value; map //如果没找到,直接跳过 if(it == valueToCount.end()) { pPrevious = pNode; pNode = pNode->pNext; continue; } int count = it->second;
//如果出现次数为1,无需删除,直接跳过 if(count == 1) { pPrevious = pNode; pNode = pNode->pNext; continue; }
//说明有重复节点,删除当前节点,这里设置之前的节点保留下来。删除节点的时候,要不要使得previous变更(不需要) if(count > 1) { Node* pDeleteNode = pNode; pNode = pNode->pNext; pPrevious->pNext = pNode; delete pDeleteNode;
//更新出现次数 valueToCount[value] = count - 1; } } } |
12 |
找出单向链表中倒数第k个节点 实现一个算法,找出单项链表中倒数第k个结点
输入: 7(总节点个数) 3(k,倒数第几个节点) 1 2 3 4 5 6 7 输出: 5 |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53780983 题目3与这一题是同一个题目,以这题的答案为准 分析:这个应该设置两个指针, 举个例子:一共6个节点,找到倒数第2个节点, 实际上找到的就是第5个节点= N - k + 1 最好的方式就是,一个指针找到第5个节点的时候,另一 指针刚好到第6个节点 1 2 3 4 5 6 第一个指针走到N-k+1时,第二个指针走到N 两者相差步数=N - (N-k+1) = k - 1,也就是第一个指针 先走(k-1)步后,两个指针一起走 即先走1步 N=7,k=3时
关键: 1 倒数第k个节点=正数第(N-k+1)个节点 第二个指针走到N-k+1时,第一个指针走到N 2 第一个指针比第二个指针提前走= N - (N-k+1)= k-1步 3 1 <= k <= N,如果k>N,则无法成立
代码:
Node* findKNode(Node* pHead , int k , int n) { if(NULL == pHead) { return NULL; } if( k > n || k < 1) { return NULL; }
//设置两个指针,第一个指针先走k-1步,然后两个指针一起走,注意头指针不算 Node* pFast = pHead; int count = 0; while(pFast) { if(count == k-1) { break; } count++; pFast = pFast->pNext; } Node* pSlow = pHead; while(pFast->pNext != NULL) { pFast = pFast->pNext; pSlow = pSlow->pNext; } return pSlow; } |
13 |
想像有个Web服务器,实现简化版搜索引擎。这套系统有100台机器来响应搜索查询,可能会对另外的机器集群调用processSearch(string query)以得到真正的结果。响应查询请求的机器是随机挑选的,因此两个同样地请求不一定由同一台机器响应。方法processSearch的开销很大,请设计一种缓存机制,缓存最近几次查询的结果。当数据发生变化时,务必说明该如何更新缓存。 |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/54348791 关键 1: 单系统缓存:要求:1】根据键找到值,2】容易删除旧数据 解决办法:1】创建链表,每次访问的结点移动到链表首部。 2】创建散列表,根据键找到链表中的结点 关键:这个缓存实际上是一个链表,只不过能进行几种操作,还包含了查询到结点的映射 map Node* _head ; Node* _tail; 2:多机器的缓存设计 3种方案: 1】每个机器都存储各自缓存,优点:快速,重复查询被认为全新查询 2】每个机器持有缓存副本,原理:新的条目添加至缓存,会发送给所有机器,缺点:每个条目占用空间是做法1的N倍,实际存的缓存少 3】每台机器存储缓存不同部分,原理:新的查询请求query发送给机器i,机器i根据hash(query) % n = j,得到该查询的缓存应该在机器j 上,转发给机器j做处理,机器j如果能在缓存中直接找到就直接返回;否则调用processSearch 进行查询,更新缓存,并将结果返回给机器i 优点:每台机器只负责缓存的一部分,可容纳更多缓存 缺点:存在机器间的跳转 3:内容改变时更新结果 1】网址内容变化 2】搜索结果的排序变化 3】特定查询出现新页面 1】和2】建立散列表,指示缓存查询与特定网址相关 不要求实时刷新,则可以开启定时任务,遍历所有机器缓存,将更新过网址的相关联结果清除 或者设置缓存失效时间来定期刷新 4:改进 热门查询只转发一次后就自己存储结果;根据网址或主题实现不同的逾期查询
代码: template class Node { Node(){} Node* _next; T _result; };
//缓存,实际是一个链表,可以将当前访问过的放在链表首部,超出个数,删除尾部结点 template class Cache { public: Cache():_maxSize(10){} //析构的时候,释放整个链表,先将映射清空,再清空链表 ~Cache() { _queryToNode.clear(); releaseList(Node* head); }
void releaseList(Node* head) { if(NULL == head) { return; } }
//将链表中某结点移至头部 void moveToFront(Node* node) { }
//根据查询字符串,将链表中某结点移至头部 void moveToFront(string& query) { }
void deleteTailNode(Node* tail) { if(_count > _maxSize) { delete tail; tail = NULL; } }
//删除超过缓存个数的结点 void deleteNode() { //下面判断插入后结点个数是否超过,超过就删除尾部结点,删除当前结点,需要判断其前面一个结点 int count = 1; if(_count > _maxSize) { //如果只有一个结点,肯定是不需要删除的 if(_count <= 1) { return; } Node* curNode = _head->_next; Node* preNode = _head while(curNode) { count++; //超出部分结点需要删除 if(count > _maxSize) { Node* deleteNode = curNode; //清空映射中该结点对应映射 preNode->_next = curNode->_next; //更新结点 preNode = curNode->_next; curNode = curNode->_next; delete deleteNode; deleteNode = NULL; count--; } //正常情况就直接遍历 else { preNode = curNode; curNode = curNode->_next; } } } }
//获取结果,先查询是否在映射中存在,如果存在,就直接返回;否则,新建结点并插入首部,并判断结点个数是否符合要求 T getResult(string& query) { map if(it != _queryToNode.end() ) { Node* node = it->second; //将结点放入首部 moveToFront(node); return node->_result; } //如果没有查询到结果,直接返回为空 else { return NULL; } }
//这里插入的是:查询字符串记其结果 void insertQueryResult(string& query , T result) { //如果该查询对应结果已经存在,就更新 map if(it != _queryToNode.end() ) { Node* node = it->second; //将结点放入首部,需要判断当前放在顶部的是不是尾结点,如果是尾结点,那么需要重新设置尾结点 moveToFront(node); node->_result = result; } //如果结果不存在,那么就新建一个结点并放在首部 else { //如果是第一次建立结点,那么尾结点等于首结点 bool isFirst = false; if(NULL == _head) { isFirst = true; } Node* node = new Node(); //让该节点指向首结点,并让首结点等于该节点 node->_next = _head; node->_result = result; //将结果插入映射中 _queryToNode.insert(pair
//初始化首尾结点 if(isFirst) { _tail = _head; } _count++; //判断是否需要删除结点,其实只需要删除尾部结点 deleteTailNode(_tail); } }
public: map Node* _head ; Node* _tail; int _maxSize; int _count;//统计链表结点个数 }; |
14 |
以给定值x为基准将链表分割成两部分 以给定值x为基准将链表分隔成两部分,所有小于x的结点排在大于或等于x的结点之前
|
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53783044 书上解法: 1 新建两个链表,将
代码:
Node* partitionNode(Node* pHead , int x ,Node* head1 , Node* head2 ) { //Node* head1 = new Node(); head1->pNext = NULL; //Node* head2 = new Node(); head2->pNext = NULL; Node* pNode = pHead->pNext; while(pNode) { Node* next = pNode->pNext; if(pNode->value < x) { //头插法,作为头结点,这里直接使下一个节点为头结点 pNode->pNext = head1; head1 = pNode; } else { //头插法,作为头结点 pNode->pNext = head2; head2 = pNode; } pNode = next; }
//最后将链表2拼接在链表1后面,先寻找到链表1的尾部 Node* pLast = head1; Node* pPrevious = NULL;
//注意最后一个节点的pNext为空 while(pLast->pNext) { pPrevious = pLast; pLast = pLast->pNext; } //此时pPrevious就是最后一个节点 pPrevious->pNext = head2; return head1; } |
15 |
对两个用链表表示的整数求和 给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。
输入: 123 789 123 89 输出: 912 212 |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53784998 分析:比如两个数分别是123,789,由于个位在首部,得到如下的两个数 3 2 1 9 8 7 2 1 9 最终结果为912,算的时候直接从左向右(从低位到高位算即可,读取的时候反向读取链表即可)
关键: 1 需要明白相加的两个整数长度可能不同,需要注意遍历
代码: //将两个链表求和,个位是在链表首位,所加结果放在第一个链表中并返回,需要放在较长的链表中(默认是第一个) Node* sum(Node* nNode,Node* mNode) { if(nNode == NULL || mNode == NULL) { return NULL; }
Node* pNNode = nNode->pNext; Node* pMNode = mNode->pNext; int pass = 0; //设置进位
//注意,这里只能有一个不为空即可,如果另一个数为空,则按0计算 while(pNNode || pMNode) { //所加结果需要进位 if(pMNode == NULL) { pNNode->value += 0 + pass; } else { pNNode->value += pMNode->value + pass; } if( pNNode->value > 10) { pNNode->value -= 10; pass = 1; } else { pass = 0; } pNNode = pNNode->pNext; if(pMNode != NULL) { pMNode = pMNode->pNext; } } return nNode; } |
16 |
给定有环链表,实现算法返回环路的开头节点 输入: 5(链表长度) 2(环的起始下标) 0 1 2 3 4(链表中每个节点的元素) 输出: 有环 环的起始地址 环的长度3 |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53787230 关键: 1 设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m 则第一次相遇时: 快指针走的距离S1=L + m + k (1) 慢指针走的距离S2=L + k (2) 满足: S1=2*S2 (3) 解方程得L = m - k 该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离 推得规律: 1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点 2 环的长度=快慢指针在相遇点处继续走再次相遇时所走的步数 2 环的起始点=头结点,相遇节点分别每次走一步后,相遇之后的节点 3 环的长度=快慢指针在相遇节点处再次相遇所走的步数
代码: //如果有环,则返回相遇的节点 Node* isHasCycle(Node* pHead) { if(pHead == NULL) { return false; } Node* pFast = pHead->pNext; Node* pSlow = pFast; while( pSlow && pFast) { pFast = pFast->pNext->pNext; pSlow = pSlow->pNext; //相遇 if(pSlow == pFast) { break; } }
//如果没有环,pFast应该已经为空了 if(pFast == NULL) { return NULL; } return pFast; }
//寻找环形链表的起始环节点,提供相遇的节点,根据 头结点距离环形节点的距离k=相遇节点距离环形节点的距离,则从头结点和相遇节点分别继续向下走,相遇处即为所求 Node* findCyclicBeginNode(Node* pHead , Node* meetNode) { Node* tempNode = meetNode; if(pHead == NULL || meetNode == NULL) { return NULL; } Node* pNode = pHead->pNext; while(pNode != meetNode) { pNode = pNode->pNext; meetNode = meetNode->pNext; } meetNode = tempNode; return pNode; }
//计算环形链表中环的长度 = 快慢指针在相遇处继续按照快慢步走后,下次相遇时所走的步数 int calculateCycleLength(Node* pHead , Node* meetNode) { if(pHead == NULL || meetNode == NULL) { return -1; } Node* pFast = meetNode->pNext->pNext; Node* pSlow = meetNode->pNext; int count = 1; while(pFast != pSlow) { pFast = pFast->pNext->pNext; pSlow = pSlow->pNext; count++; } return count; } |
17 |
检查链表是否为回文 编写一个函数,检查链表是否为回文 输入: 5(链表长度) 1 2 3 2 1 5 1 2 3 2 3 4 1 2 2 1 4 1 2 2 3 输出: yes no yes no |
程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53787704 关键: 1 可以采用翻转整个链表并比较,如果两个链表相同 2 可以采用快慢指针,将慢指针的节点存入栈中,当快指针走到链表末尾时,慢 指针走到链表中间,此时比较慢指针对应元素和栈顶元素是否相等即可,这个只需要遍历一遍
代码:
//是否是环形链表 bool isPalindromeList(Node* pHead) { stack if(pHead == NULL) { return false; } int length = 0; Node* pNode = pHead->pNext; while(pNode) { stackData.push(pNode->value); length++; pNode = pNode->pNext; }
//下面比较 pNode = pHead->pNext; int count = 0; while(pNode) { if(count >= length/2) { break; } int value = stackData.top(); stackData.pop(); if(pNode->value != value) { return false; } pNode = pNode->pNext; count++; } return true; } |
18 |
Remove Nth Node From End of List Given a linked list, remove the nth node from the end of list and return its head. For example, Given linked list: 1->2->3->4->5, and n = 2. After removing the second node from the end, the linked list becomes 1->2->3->5. Note: Given n will always be valid. Try to do this in one pass. 翻译: 这个是删除链表倒数第n个结点。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/54799052 |
19 |
Merge Two Sorted Lists Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/54799851 |
20 |
Swap Nodes in Pairs Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2->3->4, you should return the list as 2->1->4->3. Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed. 分析:交换链表中相邻两个结点,并且不是结点中的值交换。而且只能使用常量空间。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/54808155 |
21 |
Reverse Nodes in k-Group Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is. You may not alter the values in the nodes, only nodes itself may be changed. Only constant memory is allowed. For example, Given this linked list: 1->2->3->4->5 For k = 2, you should return: 2->1->4->3->5 For k = 3, you should return: 3->2->1->4->5 分析: a multiple of k:k的倍数 注意:k的倍数内的结点反转,剩余的部分保持不变。因此有点类似于: 1~k反转,k+1~2k反转。那就需要将结点分成长度为k的组,每一组组内 反转,除了首结点没有前驱外,其余结点找到前驱结点,令当前结点指向前驱结点即可。 关键只允许常量空间 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/54808155 |
22 |
Rotate List Given a list, rotate the list to the right by k places, where k is non-negative. For example: Given 1->2->3->4->5->NULL and k = 2, return 4->5->1->2->3->NULL. 分析:题目的意思应该是将链表最右侧的结点转转到最左边,重复这样的操作k次。此题可以等效替换成寻找到倒数第k个结点, |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/54980834 |
23 |
Remove Duplicates from Sorted List II Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. For example, Given 1->2->3->3->4->4->5, return 1->2->5. Given 1->1->1->2->3, return 2->3. 分析:这道题目实际上就是要删除所有重复元素对应的所有节点。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55008663 |
24 |
Remove Duplicates from Sorted List Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given 1->1->2, return 1->2. Given 1->1->2->3->3, return 1->2->3. 分析:删除链表中的冗余元素,多个元素只保留一个。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55012234 |
25 |
Partition List Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. You should preserve the original relative order of the nodes in each of the two partitions. For example, Given 1->4->3->2->5->2 and x = 3, return 1->2->2->4->3->5. 分析:题目给定一个链表和一个值,用该值进行划分,所有小于该值的数都出现在大于等于该值的数的前面 ,而且需要保持两个划分中相对顺序不能改变 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55049549 |
26 |
Reverse Linked List II Reverse a linked list from position m to n. Do it in-place and in one-pass. For example: Given 1->2->3->4->5->NULL, m = 2 and n = 4, return 1->4->3->2->5->NULL. Note: Given m, n satisfy the following condition: 1 ≤ m ≤ n ≤ length of list. 分析:逆置指定两个位置及其中间的结点。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55061701 |
27 |
Copy List with Random Pointer A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list. |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55667027 |
28 |
Linked List Cycle Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space? 分析:分析一个链表是否有环, |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55668626 |
29 |
Linked List Cycle II Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Note: Do not modify the linked list. Follow up: Can you solve it without using extra space? 分析:给定一个链表,返回环开始的那个节点,如果没有环,返回空。 不要修改链表。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55669703 |
30 |
Reorder List Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do this in-place without altering the nodes' values. For example, Given {1,2,3,4}, reorder it to {1,4,2,3}. 分析:此题就是要将最后面的结点插在第一个结点后面,倒数第二个结点插在第二个结点后面 且不允许修改结点的值。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55669966 |
31 |
LRU Cache Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. Follow up: Could you do both operations in O(1) time complexity? Example: LRUCache cache = new LRUCache( 2 capacity ); 分析:最近最少使用缓存。题目要求能够将最近最不经常使用的元素能够在缓存超出容量的条件下 在O(1)时间内删除,如果缓存不存在,那么插入缓存:耗费的时间也是O(1)。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55706176 |
32 |
Insertion Sort List Sort a linked list using insertion sort. 分析:用插入排序对一个链表排序。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/55790432 |
33 |
Sort List Sort a linked list in O(n log n) time using constant space complexity. 分析:在O(NlogN)时间内使用常量空间对一个链表排序 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56016376 |
34 |
Intersection of Two Linked Lists Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3 begin to intersect at node c1. Notes: If the two linked lists have no intersection at all, return null. The linked lists must retain their original structure after the function returns. You may assume there are no cycles anywhere in the entire linked structure. Your code should preferably run in O(n) time and use only O(1) memory. Credits: Special thanks to @stellari for adding this problem and creating all test cases. 分析:写一个程序,判断两个链表交叉处的第一个结点,如果没有交叉,返回NULL。不能改变原有结构 必须在O(n)时间内完成,使用O(1)的空间。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56039127 |
35 |
Remove Linked List Elements Remove all elements from a linked list of integers that have value val. Example Given: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6 Return: 1 --> 2 --> 3 --> 4 --> 5 分析:删除链表中等于指定元素的所有结点。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56329681 |
36 |
Reverse Linked List Reverse a singly linked list. click to show more hints. Hint: A linked list can be reversed either iteratively or recursively. Could you implement both? 分析:逆置一个单链表。最好能迭代逆置或递归逆置。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56480559 |
37 |
Palindrome Linked List Given a singly linked list, determine if it is a palindrome. Follow up: Could you do it in O(n) time and O(1) space? 分析:给定单链表,能否在O(n)时间和O(1)空间确定其是否是回文的。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56695581 |
38 |
Delete Node in a Linked List Write a function to delete a node (except the tail) in a singly linked list, given only access to that node. Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function. 分析:此题是给定一个结点(不是尾结点),要删除该结点 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56842646 |
39 |
Odd Even Linked List Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes. You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity. Example: Given 1->2->3->4->5->NULL, return 1->3->5->2->4->NULL. Note: The relative order inside both the even and odd groups should remain as it was in the input. The first node is considered odd, the second node even and so on ... 分析:题目要求将所有偶数的结点放置在所有奇数结点的后面, 并且偶数结点和奇数结点本身各自维持自己的相对顺序不变。 在常量空间复杂度和线性时间复杂度下求解。 |
Leecode https://blog.csdn.net/qingyuanluofeng/article/details/59038740 |
40 |
如何实现链表的逆序 给定一个带头结点的单链表,请将其逆序,即如果单链表原来为: head->1->2->3->4->5->6->7,那么逆序后变为 head->7->6->5->4->3->2->1 |
https://blog.csdn.net/qingyuanluofeng/article/details/89305686 |
41 |
如何从无序链表中移除重复项 给定一个没有排序的链表,去掉其重复项,并保留原顺序,例如链表 1->3->1->5->5->7, 去掉重复项后变为1->3->5->7 |
https://blog.csdn.net/qingyuanluofeng/article/details/89325898 |
42 |
如何计算两个单链表所代表的数之和 给定两个单链表,链表的每个节点代表一位数,计算两个数的和。例如: 输入链表(3->1->5)和链表(5->9->2),输出: 8->0->8,即513+295=808,注意个位数在链表头 |
https://blog.csdn.net/qingyuanluofeng/article/details/89348759 |
43 |
如何对链表进行重新排序 给定链表L0->L1->...Ln-1->Ln,把链表重新排序为L0->Ln->L1->Ln-1->L2->Ln-2...。 要求: (1) 在原来链表的基础上进行排序,即不能申请新的结点; (2) 只能修改结点的next域,不能修改数据域。 |
https://blog.csdn.net/qingyuanluofeng/article/details/89393090 |
44 |
如何找出单链表中的倒数第k个元素 找出单链表中的倒数低k个元素,例如给定单链表: 1->2->3->4->5->6->7,则单链表的倒数第k=3个元素为5 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/89630808 |
45 |
如何检测一个较大的单链表是否有环 单链表有环指的是单链表中某个结点的next域指向的是链表中在它之前的某一个结点, 这样在链表的尾部形成一个环形结构。如何判断单链表是否有环存在 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/89668102 |
46 |
如何把链表相邻元素翻转 把链表相邻元素翻转,例如给定链表为 1->2->3->4->5->6->7,则翻转后的链表变为 2->1->4->3->6->5->7 |
https://blog.csdn.net/qingyuanluofeng/article/details/89873662 |
47 |
如何把链表以K个节点为一组进行翻转 假设给定链表1->2->3->4->5->6->7和一个数K,如果K的值为2,那么翻转后的链表为 2->1->4->3->6->5->7。如果K的值为3,那么翻转后的链表为: 3->2->1->6->5->4->7。 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/89930863 |
48 |
如何合并两个有序链表 已知两个链表head1和head2各自有序(例如升序排序),请把它们合并成一个链表, 要求合并后的链表仍然有序。 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/89944625 |
49 |
如何在只给定单链表中某个结点的指针的情况下删除该结点 假设给定链表1->2->3->4->5->6->7中指向第5个元素的指针,要求把结点5删掉,删除后链表变为 1->2->3->4->6->7。 |
https://blog.csdn.net/qingyuanluofeng/article/details/89944687 |
50 |
如何判断两个链表(无环)是否交叉 单链表相交指的是两个链表存在完全重合的部分,如下图所示: head1->1->2->3->4->5->6->7 head2->1->2->3->4 在上图中,这两个链表相交于结点5,要求判断两个链表是否相交,如果相交,找出相交处的结点。 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/90109288 |
51 |
如何展开链接列表 给定一个有序链表,其中每个节点也表示一个有序链表,结点包含两个类型的指针: (1) 指向主链表中下一个结点的指针(在下面的代码中称为正确指针) (2) 指向此结点头的链表(在下面的代码中称为down指针) 所有链表都被排序。请参见以下示例: 3 -> 11 -> 15 -> 30 |V |V |V |V 6 21 22 39 |V |V |V 8 50 40 |V |V 31 55 实现一个函数flatten(),该函数用来将链表扁平化成单个链表,扁平化的链表也应该被排序。 例如,对于上述输入链表,输出链表应该为3->6->8->11->15->21->22->30->31->39->40->50->55 |
Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/90114758 |
52 |
移动小球 我有小球,从左到右一次编号为1,2,3,...,n 你可以执行两种指令: A X Y表示把小球X移动到小球Y的左边,B X Y表示把小球X移动到小球Y的右边。指令保证合法,即X不等于Y。 输入小球个数n,指令条数m和m条指令,从左到右输出最后的序列。注意,n可能高达500000,而m可能高达100000 输入: 6 2 A 1 4 B 3 5 输出: 2 1 4 5 3 6 |
算法竞赛入门经典 https://blog.csdn.net/qingyuanluofeng/article/details/47730725 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
参考:
[1]计算机考研--机试指南,王道论坛 组编
[2]剑指offer
[3]算法设计与分析
[4]编程之美
[5]程序员面试金典
[6]leecode
[7]Python程序员面试算法宝典
[8]刘汝佳算法竞赛入门经典
[9]算法导论
[10]编程珠玑