十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
关于链表
成都创新互联专注于新宾企业网站建设,响应式网站建设,商城网站制作。新宾网站建设公司,为新宾等地区提供建站服务。全流程定制设计,专业设计,全程项目跟踪,成都创新互联专业和态度为您提供的服务
链表是一种动态的数据结构,因为在创建链表时无需知道链表的长度,当插入一个节点时,只需要为新的节点分配内存,其空间效率和数组相比要高,但是每次都要创建空间所以时间不是很高
单向链表定义节点
struct ListNode { int m_value; ListNode* m_pNext; };
在链表后面添加节点
void AddToTail(ListNode** pHead,int value)//时间效率O(N) { ListNode* pnewNode=new ListNode();//创建新结点 pnewNode->m_value=value; pnewNode->m_pNext=NULL; if(pHead==NULL)//当头结点为空 { pHead=pnewNode; } else//不为空 { ListNode*pNode=*pHead; while(pNode->m_next!=NULL)//遍历找到最后一个节点 { pNode=pNode->m_next; } pNode->m_next=pnewNode; } }
删除其中某节点
void RemoveNode(ListNode**pHead, int value) { if (pHead == NULL||*pHead==NULL)//头结点为空 return; ListNode*pDelete = NULL; if ((*pHead)->m_value == value) { pDelete = *pHead; *pHead = (*pHead)->m_pNext; } else { ListNode*pNode = *pHead; //遍历查找要删除结点的前一个结点 while (pNode->m_pNext!=NULL &&pNode->m_pNext-> m_value != value) { pNode = pNode->m_pNext; } pDelete = pNode->m_pNext; pNode->m_pNext = pDelete->m_pNext; if (pDelete != NULL) { delete pDelete; pDelete = NULL; } }
题目1:
输入一个链表头结点,从头到尾反过来打印这个结点值(不改变结点结构)
程序1.0
若链表为空直接返回链表,若只有一个节点,打印此结点;若有多个结点,设置一个计数器,先、遍历结点统计结点个数,再循环结点数次,在这个循环里再依次找到倒数第一个、倒数第二个、倒数第N个节点
void PrintListReverse(ListNode*pHead)//时间复杂度O(N^2) { while (pHead == NULL)//没有结点 return; if (pHead->m_pNext == NULL)//一个节点 { cout << pHead->m_value; return; } ListNode*pNode = pHead; int count =1; while (pNode->m_pNext != NULL) { count++; pNode = pNode->m_pNext; } while (count != 0) { for (int i = 1; i < count; i++) { pNode = pNode->m_pNext;//找到倒数第N个节点的前一个结点 } cout << pNode->m_value << "->"; count--; } }
程序2.0
上面用循环实现,过于复杂且效率不高,所以第二次使用栈来完成,栈有“先入后出”的特性,所以用栈来实现更为方便,没遇到一个不为空的结点就把它放到栈中
void PrintListReverse(ListNode*pHead)//时间复杂度O(N) { stacks1; ListNode*pNode = pHead; while (pNode->m_pNext != NULL) { s1.push(pNode); pNode = pNode->m_pNext; } while (!s1.empty()) { cout << s1.top()->m_value << "->"; s1.pop(); } }
程序3.0
我们知道递归的本质就是栈,所以我们也可以用栈来实现它,先递归后面的结点,一直递归到没有结点,再输出该结点
void PrintListReverse(ListNode*pHead) { if (pHead != NULL) { if (pHead->m_pNext != NULL)//递归停止条件 { PrintListReverse(pHead->m_pNext); } cout << pHead->m_value << "->"; } }
递归写法虽然看起来要简洁的多,但是若结点超级长则会导致函数调用栈溢出,所以在实现是最好使用显示调用栈的方式来实现
题目2:
题目:在O(1)的时间删除链表结点
分析:要删除一个节点,O(n)的方法是从头到尾遍历找到要删除的结点(即顺序查找),并在链表中删除它。
程序1.0 删除一个节点复杂度O(n)
void DeleteNode(ListNode**pListHead, ListNode* pToBeDeleted) { if (pListHead || pToBeDeleted) return; ListNode*cur = *pListHead; while (cur->m_pNext != pToBeDeleted) { cur = cur->m_pNext; } cur->m_pNext = pToBeDeleted->m_pNext; delete pToBeDeleted; }
程序2.0
删除一个结点复杂度O(1),找到要删除的结点pToBeDelete的后面的结点del,把这个结点的值覆盖要删除的结点,删除这个后面的结点
void DeleteNode(ListNode**pListHead, ListNode* pToBeDeleted) { if (pListHead || pToBeDeleted)//若为空 return; if ((*pListHead == pToBeDeleted) && (pToBeDeleted->m_pNext == NULL))//只有一个节点且删除这个结点 { delete pToBeDeleted; pToBeDeleted = NULL; *pListHead = NULL; return; } if (pToBeDeleted->m_pNext == NULL)//有多个结点pToBeDeleted为尾节点O(N) { ListNode*cur = *pListHead; while (cur->m_pNext != pToBeDeleted) { cur = cur->m_pNext; } cur->m_pNext = NULL; delete pToBeDeleted; pToBeDeleted = NULL; } else if (pToBeDeleted->m_pNext)//有多个结点且pToDeleted不为尾 { ListNode*del = pToBeDeleted->m_pNext; pToBeDeleted->m_value = del->m_value; pToBeDeleted->m_pNext = del->m_pNext; delete del; del = NULL; } }
题目3:
输入一个链表,输出链表中倒数第K个节点
分析:定义两个指针均指向头,一个指针先走K步,另一个等第一个指针走k步后再和其一起走,知道第一个走到空时第二个指针即走到倒数第k个节点
考虑:1.输入的头结点是否为空,访问空指针指向的内存导致程序奔溃2.链表的结点是否大于k3.k若为0怎么办
ListNode* FindKthToTail(ListNode*pHead, int k) { if (pHead == NULL || k == 0) return NULL; ListNode *fast = pHead; ListNode*slow = pHead; while (--k) { if (fast->m_pNext != NULL) fast = fast->m_pNext; else return NULL; } while (fast->m_pNext) { slow = slow->m_pNext; fast = fast->m_pNext; } return slow; }
相似快慢指针问题
1.题目:求链表中间结点
设置指向头的快、慢指针,快指针每次走两步,慢的每次走一步,当快指针走到末尾时,慢指针刚好在中间
ListNode*FindMidNode(ListNode*pHead) { if (pHead == NULL) return NULL; ListNode*fast = pHead; ListNode*slow = pHead; while (fast->m_pNext&&fast) { fast = fast->m_pNext->m_pNext; slow = slow->m_pNext; } return slow; }
2.判断一个指针是否构成环形
同上方法,如果带环,则快指针一定会追上慢指针,若快指针走到了链尾(NULL),则不是环形链表
ListNode*IsCircleList(ListNode*pHead) { if (pHead == NULL) return NULL; ListNode*fast = pHead; ListNode*slow = pHead; while (fast&&fast->m_pNext) { fast = fast->m_pNext->m_pNext; slow = slow->m_pNext; if (slow == fast) return slow; } return NULL; }
题目4:
反转链表
void ReverseList(ListNode*pHead) { if (pHead == NULL||pHead->m_pNext==NULL) return; ListNode*newHead = NULL; ListNode*cur = pHead; ListNode*prev = pHead; while (cur) { prev = cur; cur = cur->m_pNext; prev->m_pNext = newHead; newHead = prev; } pHead = newHead; }
题目5:
合并两个排序的链表
ListNode*Merge(ListNode*pHead1, ListNode*phead2) { if (pHead1 == NULL) return phead2; if (phead2 == NULL) return pHead1; ListNode*newHead = NULL; while (pHead1&&phead2) { if (pHead1->m_value < phead2->m_value) { newHead = pHead1; newHead->m_pNext = Merge(pHead1->m_pNext, phead2); } else { newHead = phead2; newHead->m_pNext = Merge(pHead1, phead2->m_pNext); } } return newHead; }