链表算法实现

目录

合并两个有序链表

合并k个有序链表

链表奇偶重排

删除链表中倒数第K个节点

判断链表是否有环

反转链表

反转给定范围内的链表

链表回文判断

链表删除重复元素


合并两个有序链表

注意点: 使用一个节点记住新链表的头
/**
 * 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值