目录
合并两个有序链表
注意点:
使用一个节点记住新链表的头
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode();
ListNode curr = head;
while(l1 != null && l2 != null){
if(l1.val > l2.val){
curr.next = l2;
l2 = l2.next;
}else{
curr.next = l1;
l1 = l1.next;
}
curr = curr.next;
}
curr.next = l1 != null ? l1 : l2;
return head.next;
}
}
合并k个有序链表
分治合并思路解析
代码实现
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return merge(lists, 0, lists.length - 1);
}
public ListNode merge(ListNode[] lists, int l, int r) {
if (l == r) {
return lists[l];
}
if (l > r) {
return null;
}
int mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode();
ListNode curr = head;
while(l1 != null && l2 != null){
if(l1.val > l2.val){
curr.next = l2;
l2 = l2.next;
}else{
curr.next = l1;
l1 = l1.next;
}
curr = curr.next;
}
curr.next = l1 != null ? l1 : l2;
return head.next;
}
}
链表奇偶重排
思路:
将偶数从原链表删除,并将偶数新组成一个链表,最后跟在奇数链表的后面
偶数链表最后一个元素处理比较复杂
思路改进:按照上述想法,
以偶数链表为主循环,两条链表相互依赖并拆分
class Solution {
public ListNode oddEvenList(ListNode head) {
if (head == null) {
return head;
}
ListNode evenHead = head.next;
ListNode odd = head, even = evenHead;
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
}
删除链表中倒数第K个节点
思路:前后指针,仅遍历一次,时间复杂度O(L)
注意点:使用一个空节点作为链表头结点,兼容链表头删。后指针从空节点开始后移,并且前指针判空,最终定位到需要删除节点的前一个节点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null || n <= 0){
return head;
}
ListNode ans = new ListNode(0);
ans.next = head;
ListNode first = head;
ListNode second = ans;
while(n-- > 0){
first = first.next;
}
while(first != null){
first = first.next;
second = second.next;
}
second.next = second.next.next;
return ans.next;
}
}
判断链表是否有环
思路1:哈希表,使用HashSet不允许元素重复的特性,将节点本身存储
时间复杂度、空间复杂度均为O(n)
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet();
while(head != null){
if(!set.add(head)){
return true;
}
head = head.next;
}
return false;
}
}
思路2:快慢指针,快指针每次走两个,慢指针每次走一个。如果链表有环,那么快指针就能追上慢指针;如果链表无环那么fast必定会走到null节点
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode fast = head.next;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
}
反转链表
思路:每次 while 仅反转pre与curr的指针方向
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while(curr != null){
ListNode next = curr.next;
curr.next = pre; //每次 while 仅反转pre与curr的指针方向
pre = curr;
curr = next;
}
return pre;
}
}
反转给定范围内的链表
思路:将需要反转的链表段放入栈中,然后拿出进行前后拼接,
注意的点:需要兼容整个链表反转的情况,需要有一个空节点来兼容
时间复杂度:O(n),空间复杂度O(n):right - left
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
Stack<ListNode> stack = new Stack<>();
ListNode res = new ListNode(0, head);
ListNode pre = res;
//定位到需要反转的前一个节点
int i = 1;
for(; i < left && pre.next != null; ++i){
pre = pre.next;
}
if(pre.next == null){
return head;
}
//将需要反转返回的节点push到栈中,记得设置next为null,否则会链表成环
ListNode curr = pre.next;
ListNode tmp = null;
while(i <= right && curr != null){
tmp = curr.next;
curr.next = null;
stack.push(curr);
curr = tmp;
i++;
}
//将栈中节点拿出并拼接前后
ListNode after = curr;
while(!stack.isEmpty()){
pre.next = stack.pop();
pre = pre.next;
}
pre.next = curr;
return res.next;
}
}
链表回文判断
思路:1、找出链表的中心节点的前一个节点;2、将链表分为前后两个子链表;3、反转后面的子链表;4、两个链表比较
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null){
return true;
}
//找出中间节点的前一个
ListNode slow = head;
ListNode fast = head;
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
//拆分为两个list
ListNode list1 = head;
ListNode list2 = slow.next;
slow.next = null;
//反转list2
ListNode pre = null;
ListNode curr = list2;
while(curr != null){
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
//比较
while(pre != null){
if(pre.val != list1.val){
return false;
}
pre = pre.next;
list1 = list1.next;
}
return true;
}
}
链表删除重复元素
思路:一层循环,使用一个变量记录需要比较的值
{1,2,2} -> {1,2}
{1,1,2} -> {1,2}
public class Solution {
/**
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode deleteDuplicates (ListNode head) {
// write code here
if(head == null){
return null;
}
int value = head.val;
ListNode curr = head;
while(curr.next != null){
if(curr.next.val == value){
curr.next = curr.next.next;
}else{
value = curr.next.val;
curr = curr.next;
}
}
return head;
}
}
引入单链表操作可见:单链表操作
#pragma once//防止头文件重定义
#include<stdio.h>
#include<Windows.h>
#include<assert.h>
typedef int DataType;
typedef struct CLinkList{
struct CLinkList *pNext;
struct CLinkList *pRandom;
DataType data;
}CLinkList;
#include"sLinkList.h"
#include"ComplexLinkList.h"
//从尾到头打印单链表(不带头、无循环)
//方法1:每次遍历找出pEnd
void PrintR(SLinkListNode *pFirst)
{
SLinkListNode *pNode, *pEnd = NULL;
pNode = pFirst;
while (pEnd != pFirst){
while (pNode->pNext != pEnd){
pNode = pNode->pNext;
}
pEnd = pNode;
printf("%d -> ", pNode->data);
pNode = pFirst;
}
printf("NULL\n");
}
//方法2:递归
void PrintD(SLinkListNode *pNode)
{
if (pNode->pNext){
PrintD(pNode->pNext);
}
printf("%d ", pNode->data);
}
//逆置单链表
SLinkListNode * ReverseList(SLinkListNode *pFirst)
{
//方法1:原链表头删+新链表头插
/*SLinkListNode *pNewFirst = NULL;
SLinkListNode *pNode;
pNode = pFirst;
while (pNode){
SLinkListPushFront(&pNewFirst, pNode->data);
pNode = pNode->pNext;
PopFront(&pFirst);
}
return pNewFirst;*/
//方法2:三个指针遍历
SLinkListNode *pre = NULL;
SLinkListNode *pNode = pFirst;
SLinkListNode *next = pNode->pNext;
while (pNode){
pNode->pNext = pre;
pre = pNode;
pNode = next;
if (next){
next = next->pNext;
}
else{
next = NULL;
}
}
return pre;
}
//删除一个无头单链表的非尾结点(不能遍历链表)
void DeleteNode(SLinkListNode *pPos)
{
//将pPos的pNext的data赋给pPos结点的data
//删除pPos的后一个结点
SLinkListNode *pNode;
pPos->data = pPos->pNext->data;
pNode = pPos->pNext->pNext;
free(pPos->pNext);
pPos->pNext = pNode;
}
//在无头单链表的一个节点前插入一个节点(不能遍历链表)
void InsertNode(SLinkListNode *pPos, DataType data)
{
//方法原理同上
//在当前节点位置的后面插入一个节点,交换data
SLinkListNode *pNewNode = CreatNewNode(data);
pNewNode->data = pPos->data;
pNewNode->pNext = pPos->pNext;
pPos->pNext = pNewNode;
pPos->data = data;
}
//合并两个有序链表,合并后依然有序
SLinkListNode * MergeSort(SLinkListNode *List1, SLinkListNode *List2)
{
SLinkListNode *pNode1 = List1;
SLinkListNode *pNode2 = List2;
SLinkListNode *pNewFirst = NULL;
while (pNode1 != NULL && pNode2 != NULL){
if (pNode1->data < pNode2->data){
SLinkListPushBack(&pNewFirst, pNode1->data);
pNode1 = pNode1->pNext;
}
else if (pNode1->data > pNode2->data){
SLinkListPushBack(&pNewFirst, pNode2->data);
pNode2 = pNode2->pNext;
}
else{
SLinkListPushBack(&pNewFirst, pNode1->data);
pNode1 = pNode1->pNext;
}
}
if (pNode1){
while (pNode1){
SLinkListPushBack(&pNewFirst, pNode1->data);
pNode1 = pNode1->pNext;
}
}
else{
while (pNode2){
SLinkListPushBack(&pNewFirst, pNode2->data);
pNode2 = pNode2->pNext;
}
}
return pNewFirst;
}
//求已排序的两个链表中相同的数据
void GetSameSet(SLinkListNode *List1, SLinkListNode *List2)
{
if (List1 == NULL || List2 == NULL){
printf("Null");
return;
}
SLinkListNode *pNode1 = List1;
SLinkListNode *pNode2 = List2;
while (pNode1 && pNode2){
if (pNode1->data == pNode2->data){
printf("%d ", pNode1->data);
pNode1 = pNode1->pNext;
pNode2 = pNode2->pNext;
continue;
}
else if (pNode1->data < pNode2->data){
pNode1 = pNode1->pNext;
}
else{
pNode2 = pNode2->pNext;
}
}
}
//单链表实现约瑟夫环
SLinkListNode * JosephCircle(SLinkListNode *pFirst, int num)
{
//单链表形成环
assert(pFirst);
SLinkListNode *pNode = pFirst;
while (pNode->pNext){
pNode = pNode->pNext;
}
pNode->pNext = pFirst;
SLinkListNode *pDNode = pFirst;
SLinkListNode *pPreNode = pDNode;
//根据num删除结点
while (pPreNode->pNext != pPreNode){
for (int i = 0; i < num; i++){
pPreNode = pDNode;
pDNode = pDNode->pNext;
}
pPreNode->pNext = pDNode->pNext;
free(pDNode);
pDNode = pPreNode->pNext;
}
return pPreNode;
}
//查找单链表的中间节点,要求只能遍历一次单链表
SLinkListNode * FindMiddle(SLinkListNode *pFirst)
{
//快慢指针,块指针是慢指针的二倍,快指针走到末尾,慢指针就指向中间节点
assert(pFirst);
SLinkListNode *pFast = pFirst;
SLinkListNode *pSlow = pFirst;
while (pFast){
pSlow = pSlow->pNext;
pFast = pFast->pNext;
if (!pFast){
break;
}
pFast = pFast->pNext;
}
return pSlow;
}
//查找单链表的倒数第k个节点,要求只能遍历一次单链表
SLinkListNode * FindLastkNode(SLinkListNode *pFirst, int k)
{
//前后指针,两个相同的指针,相隔k个节点一次遍历
assert(pFirst);
SLinkListNode *pPre = pFirst;
SLinkListNode *pBack = pFirst;
for (int i = 0; i < k; i++){
pBack = pBack->pNext;
}
while (pBack){
pPre = pPre->pNext;
pBack = pBack->pNext;
}
return pPre;
}
//删除链表倒数第k个节点
SLinkListNode * DeteleLastkNode(SLinkListNode *pFirst, int k)
{
//前后指针,记住前指针的前一个指针
assert(pFirst);
if (k == 1){
PopBack(&pFirst);
}
else{
SLinkListNode *pPreD = pFirst;
SLinkListNode *pPre = pFirst;
SLinkListNode *pBack = pFirst;
for (int i = 0; i < k; i++){
pBack = pBack->pNext;
}
while (pBack){
pPreD = pPre;
pPre = pPre->pNext;
pBack = pBack->pNext;
}
pPreD->pNext = pPre->pNext;
free(pPre);
}
return pFirst;
}
//复杂链表的复制
CLinkList * CmpLinkListCopy(CLinkList *pCFirst)
{
/*
1、复制节点(复制date和pNext),将新节点放在老节点的后面
2、遍历老节点,赋值pRandom
3、将链表拆分
*/
assert(pCFirst);
CLinkList *pNode = NULL;
CLinkList *pNewNode = NULL;
for (pNode = pCFirst; pNode; pNode = pNode->pNext->pNext){
pNewNode = (CLinkList *)malloc(sizeof(CLinkList));
assert(pNewNode);
pNewNode->data = pNode->data;
pNewNode->pNext = pNode->pNext;
pNewNode->pRandom = NULL;
pNode->pNext = pNewNode;
}
CLinkList *oldRandom = NULL;
for (pNode = pCFirst; pNode; pNode = pNode->pNext->pNext){
pNewNode = pNode->pNext;
oldRandom = pNode->pRandom;
if (oldRandom){
pNewNode->pRandom = oldRandom->pNext;
}
}
CLinkList *pNewFirst = NULL;
for (pNode = pCFirst; pNode; pNode = pNode->pNext->pNext){
pNewNode = pNode->pNext;
pNode->pNext = pNewNode->pNext;
if (pNode->pNext != NULL){
pNewNode->pNext = pNode->pNext->pNext;
}
else{
pNewNode->pNext = NULL;
}
}
return pNewNode;
}
int main()
{
CLinkList *pCFirst = NULL;
/*SLinkListNode *pFirst = NULL;
SLinkListPushFront(&pFirst, 8);
SLinkListPushFront(&pFirst, 6);
SLinkListPushFront(&pFirst, 4);
SLinkListPushFront(&pFirst, 2);
SLinkListPushFront(&pFirst, 9);
SLinkListPushFront(&pFirst, 5);
Print(pFirst);*/
//Print(DeteleLastkNode(pFirst, 2));
//Print(FindLastkNode(pFirst, 3));
//Print(FindMiddle(pFirst));
//printf("%d\n", JosephCircle(pFirst, 4)->data);
/*SLinkListNode *pFirst1 = NULL;
SLinkListPushFront(&pFirst1, 7);
SLinkListPushFront(&pFirst1, 5);
SLinkListPushFront(&pFirst1, 4);
SLinkListPushFront(&pFirst1, 1);
Print(pFirst1);
Print(MergeSort(pFirst, pFirst1));
GetSameSet(pFirst, pFirst1);*/
//PrintR(pFirst);
//PrintD(pFirst);
//Print(ReverseList(pFirst));
//DeleteNode(pFirst->pNext);
/*InsertNode(pFirst->pNext, 8);
Print(pFirst);*/
system("pause");
return 0;
}