主頁 > 軟體設計 > 單鏈表C語言實作附加力扣題

單鏈表C語言實作附加力扣題

2021-10-10 08:44:01 軟體設計

在這里插入圖片描述

目錄

  • 前言
  • 鏈表的實作
    • 介面的宣告
    • 介面的實作
    • 銷毀
    • 列印
    • 創建結點
    • 尾插
    • 頭插
    • 頭刪
    • 尾刪
    • 查找
    • 指定位置后插入
    • 指定位置前插入
    • 指定位置后洗掉
    • 移除指定位置
  • 力扣題
    • 移除鏈表元素:
    • 206. 反轉鏈表:
    • 876. 鏈表的中間結點:
    • 鏈表中倒數第k個結點:
    • 21. 合并兩個有序鏈表
    • OR36 鏈表的回文結構
    • 141. 環形鏈表
    • 138. 復制帶隨機指標的鏈表:
    • 147. 對鏈表進行插入排序

前言

今天我們的主題是實作單向不帶頭不回圈的單鏈表

鏈表的實作

介面的宣告

typedef int SLTDataTypde;

typedef struct SLTNode 
{
	SLTDataTypde data;
	struct SLTNode *next;
}SLTNode;

//鏈表銷毀
void SLTNodeDestor(SLTNode *ps);
//創建結點
SLTNode *BuySLTNode(int x);
//列印
void SLTprint(SLTNode *ps);
//尾插
void SLTPushBack(SLTNode **ps, int x);
//頭插
void SLTPushFront(SLTNode **ps,int x);
//頭刪
void SLTPopFront(SLTNode **ps);
//尾刪
void SLTPopBack(SLTNode **ps);
//查找
SLTNode* SLTFind(SLTNode *ps, int x);
//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x);
//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x);
//移除指定位置之后
void SLTEraseAfter(SLTNode *pos);
//移除指定位置之前
void SLTEraseBefor(SLTNode **ps ,SLTNode *pos);

介面的實作

#include"SLTNode.h"

//創建結點
SLTNode *BuySLTNode(int x) 
{
	SLTNode *tmp = (SLTNode *)malloc(sizeof(SLTNode));
	if (!tmp) 
	{
		perror("BuySLTNode::malloc");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;

	return tmp;
}
//列印
void SLTprint(SLTNode *ps) 
{
	
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}	

//鏈表銷毀
void SLTNodeDestor(SLTNode *ps) 
{
	assert(ps);
	while (ps) 
	{
		SLTNode *del = ps;
		ps = ps->next;
		free(ps);
	}
}
//尾插
void SLTPushBack(SLTNode **ps, int x)
{
	SLTNode *newNode = BuySLTNode(x);
	if (*ps == NULL) 
	{
		*ps = newNode;
		
	}
	else 
	{
		SLTNode *tail = *ps;
		//找尾
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
	

}

//頭插
void SLTPushFront(SLTNode **ps,int x) 
{
	
	SLTNode *newNode = BuySLTNode(x);
	newNode->next = *ps;
	*ps = newNode;
}

//頭刪
void SLTPopFront(SLTNode **ps) 
{
	if (*ps == NULL)
		return;
	SLTNode *delNode = *ps;
	*ps = delNode->next;
	free(delNode);
	delNode = NULL;

}
//尾刪
void SLTPopBack(SLTNode **ps) 
{

	SLTNode *phead = *ps;
	//只有一個結點
	if (phead->next == NULL)
	{
		free(phead);
		*ps = NULL;
		phead = NULL;
	}
	else if (*ps == NULL) 
	{
		return ;
	}
	else
	{	
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;
		//剩多個結點
		while (cur->next != NULL)
		{
			prev = cur;
			cur = cur->next;
		}
		free(cur);
		cur = NULL;
		prev->next = NULL;
	}
	
}

//查找
SLTNode* SLTFind(SLTNode *ps, int x) 
{
	if (ps == NULL)
		return NULL;
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		if (cur->data == x) 
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x) 
{
	assert(pos);
	SLTNode *next = pos->next;
	SLTNode *newNode = BuySLTNode(x);
	pos->next = newNode;
	newNode->next = next;
}

//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x)
{
	assert(pos);
	SLTNode *newNode = BuySLTNode(x);
	if (pos == *ps) 
	{
		newNode->next = *ps;
		*ps = newNode;
	}
	else 
	{
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;

		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = newNode;
		newNode->next = cur;
	}
}

//移除指定位置之后
void SLTEraseAfter(SLTNode *pos) 
{
	assert(pos);
	SLTNode *next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

//移除指定位置
void SLTEraseBefor(SLTNode ** ps, SLTNode *pos)
{
	assert(pos);
	SLTNode *next = pos->next;
	if (*ps == pos) 
	{
		free(*ps);
		*ps = NULL;
		*ps = next;
	}
	else
	{
		
		SLTNode *prev = NULL;
		SLTNode *cur = *ps;
		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}
}

銷毀

//鏈表銷毀
void SLTNodeDestor(SLTNode *ps) 
{
	assert(ps);
	while (ps) 
	{
		SLTNode *del = ps;
		ps = ps->next;
		free(ps);
	}
}

列印

請添加圖片描述

遍歷鏈表的每個結點,列印這個結點的data

//列印
void SLTprint(SLTNode *ps) 
{	
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

創建結點

//創建結點
SLTNode *BuySLTNode(int x) 
{
	SLTNode *tmp = (SLTNode *)malloc(sizeof(SLTNode));
	if (!tmp) 
	{
		perror("BuySLTNode::malloc");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;

	return tmp;
}

尾插

請添加圖片描述

創建一個新的結點newNode,找到最后一個結點的位置tail,用tail鏈接newNode,這里要注意的是我們的鏈表的實作是以不帶頭為目的的,所以會是一個空表,如果是空表的情況下,需要將ps初始化成為第一個結點,往后尾插就只需要找尾了,這里要傳二級指標,為了改變外部的指標

//尾插
void SLTPushBack(SLTNode **ps,int x) 
{
	SLTNode *newNode = BuySLTNode(x);
	//如果是空表
	if (*ps == NULL) 
	{
		*ps = newNode;
	}
	else 
	{
		//尾插找尾
		SLTNode *tail = *ps;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

傳遞一級指標無法改變main函式的plist的指標,因為是在不同的堆疊幀中開辟的,只是一份值拷貝,互不影響,所以傳遞二級指標
在這里插入圖片描述

頭插

請添加圖片描述

頭插只需要創建一個新的結點newNode,讓新的結點鏈接ps,再讓ps去做新的頭,由于需要改變外面的指標,還是要傳遞二級指標,頭插即使是面對鏈表為慷訓是不會受影響

//頭插
void SLTPushFront(SLTNode **ps,int x) 
{
	SLTNode *newNode = BuySLTNode(x);
	newNode->next = ps;
	//ps去做新的頭
	*ps = newNode;
}

頭刪

請添加圖片描述

頭刪同樣需要改變外面的指標,所以要傳二級,移除第一個結點,讓第二個結點做新的頭

//頭刪
void SLTPopFront(SLTNode **ps) 
{
	if (*ps == NULL)
		return;
	SLTNode *delNode = *ps;
	*ps = delNode->next;
	free(delNode);
	delNode = NULL;
}

尾刪

請添加圖片描述

尾刪需要考慮三種情況
1、表為空,不需要洗掉,直接回傳
2、只剩一個結點,洗掉這個結點,再置空,也需要改變外面指標的指向,必須傳二級指標
3、有多個結點,定義前驅指標prev,和目標指標cur,當cur指向最后一個結點的時候釋放cur,cur置空,前驅指標prev指向空

//尾刪
void SLTPopBack(SLTNode **ps) 
{

	SLTNode *phead = *ps;
	//只有一個結點
	if (phead->next == NULL)
	{
		free(phead);
		*ps = NULL;
		phead = NULL;
	}
	//表為空直接回傳
	else if (*ps == NULL) 
	{
		return ;
	}
	//剩多個結點
	else 
	{	
		//定義前驅指標和目標指標
		SLTNode *tail= *ps;
		SLTNode *prev = NULL;
		
		while (tail->next != NULL)
		{
			prev = tail;
			tail= tail->next;
		}
		//移除最后一個結點,讓前一個指標指向空
		free(tail);
		cur = NULL;
		prev->next = NULL;
	}
	
}

查找

查找54
請添加圖片描述

找到該結點就回傳,沒找到就回傳空,空表直接回傳

//查找
SLTNode* SLTFind(SLTNode *ps, int x) 
{
	if (ps == NULL)
		return NULL;
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		if (cur->data == x) 
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

指定位置后插入

在pos后面插入一個新的結點,需要備份pos的下一個結點,先讓newNode指向第二個結點再讓pos指向newNode

//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x) 
{
	assert(pos);
	SLTNode *next = pos->next;
	SLTNode *newNode = BuySLTNode(x);
	pos->next = newNode;
	newNode->next = next;
}

指定位置前插入

在這里插入圖片描述

1、在指定pos的前面插入,如果pos和ps在同一個位置就是頭插了
2、如果pos出現在鏈表的中間某個位置,要想在pos前插入一個新的結點就必須要找到pos前的一個結點prev,讓這個結點prev指向新的結點newNode,最后讓newNode指向當前結點

//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x)
{
	assert(pos);
	SLTNode *newNode = BuySLTNode(x);
	if (pos == *ps) 
	{
		newNode->next = *ps;
		*ps = newNode;
	}
	else 
	{
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;

		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = newNode;
		newNode->next = cur;
	}
}

指定位置后洗掉

在這里插入圖片描述

next用來保存pos位置的下一個結點,pos指向next的下一個結點,釋放next位置的結點

//移除指定位置之后
void SLTEraseAfter(SLTNode *pos) 
{
	//表為空不做處理
	if(pos == NULL)
	{
		return ;
	}
	assert(pos);
	SLTNode *next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

移除指定位置

prev和cur一起一后,當cur與pos相遇了,就將prev鏈接cur的下一個結點,釋放pos位置的結點,將指標置空

在這里插入圖片描述

如果plist和pos指向一起,就是頭刪操作了

在這里插入圖片描述

//移除指定位置
void SLTEraseBefor(SLTNode ** ps, SLTNode *pos)
{
	assert(pos);
	SLTNode *next = pos->next;
	//pos等于ps,頭刪
	if (*ps == pos) 
	{
		free(*ps);
		*ps = NULL;
		*ps = next;
	}
	else
	{
		SLTNode *prev = NULL;
		SLTNode *cur = *ps;
		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}
}

力扣題

移除鏈表元素:

點我.
在這里插入圖片描述
考慮兩種場景

  • 1、如果表的第一個結點的值就是val,那么就是頭刪了,釋放第一個結點,需要將head的指向改變,指向下一個結點
  • 2、如果表的所有結點全是val的情況,head的指向就可以一直改變,直到指向NULL,最后回傳去的就是NULL

鏈表移除結點的變形題,把特殊場景處理就能搞定

struct ListNode* removeElements(struct ListNode* head, int val){
  
    struct ListNode *prev = NULL;
    struct ListNode *cur = head;
    while(cur != NULL)
    {
    	//值為val
        struct ListNode *del = NULL;
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        //值不為val
        else
        {
            //如果cur是頭的情況
            struct ListNode *next = cur->next;
            if(prev == NULL)
            {
                free(cur);
                head = next;
                cur = next;
            }
            //cur不是頭
            else
            {
                prev->next = next;
                free(cur);
                cur = next;
            }
        }
    }
    return head;
}

第二種寫法:帶哨兵位的頭節點,可以讓prev指向哨兵位結點,這樣就不需要考慮prev是否會存在空指標的問題了,同樣的prev和cur一起一后,找到了值位val的結點,就釋放該節點,cur繼續指向下一個位置,最后只需要回傳哨兵位結點的下一個位置

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next = head;

    struct ListNode *prev = guard;
    struct ListNode *cur = head;
    while(cur != NULL)
    {
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        else
        {
            
            struct ListNode *next = cur->next;
           
            prev->next = next;
            free(cur);
            cur = next;
        }
    }
    return guard->next;
}

在這里插入圖片描述

206. 反轉鏈表:

點我.
實作思路

使用三個指標迭代的方法,prev和cur用來逆置結點的指向,next保證cur能找到下一個結點的位置,直到cur指向空了,回圈停止,回傳prev指標,整個鏈表也就逆置了

struct ListNode* reverseList(struct ListNode* head){
    if(head == NULL)
    {
        return NULL;
    }
    
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    struct ListNode* next = cur->next;

    while(cur)
    {
       //逆置
       cur->next = prev;
       //迭代
       prev = cur;
       cur = next;
       if(next != NULL)
       next = next->next;
    }

    return prev;
}

思路二:必做,鏈表頭插的變型題,將原鏈表遍歷一遍,取原鏈表的結點頭插到新的表中

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode *newhead = NULL;
  
    struct ListNode *cur = head;
    while(cur)
    {
        struct ListNode *next = cur->next;
        //頭插
        cur->next = newhead;
        newhead = cur;
        //迭代
        cur = next;
    }
    return newhead;
}

876. 鏈表的中間結點:

點我.
在這里插入圖片描述

快慢指標解法,快指標走兩步慢指標走一步,當快指標走到鏈表NULL位置的時候,或者快指標走到最后一個結點的位置,回圈終止,回傳慢指標

struct ListNode* middleNode(struct ListNode* head){
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
       
        fast = fast->next->next;
       
    }

    return slow;
}

鏈表中倒數第k個結點:

點我.
在這里插入圖片描述

快指標先走k步,快指標走完k步后,快指標和慢指標一起走,當快指標走完了,slow就是倒數第k個結點,就回傳slow的位置

極端情況:如果fast先走k步,k大于鏈表的長度,鏈表可能存在空表,是空表就回傳NULL

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;
    
    while(k--)
    {
        //k大于鏈表長度,fast已經走到NULL了,
        if(fast == NULL)
            return NULL;
        fast = fast->next;
    }
    
    while(fast)
    {
       fast = fast->next;
       slow = slow->next;
    }
    return slow;
}

21. 合并兩個有序鏈表

鏈接: 點我.
在這里插入圖片描述

思路:合并兩個鏈表只需要將原鏈表中最小的值尾插到新鏈表中去

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    if(l1 == NULL)
    return l2;
    if(l2 == NULL)
    return l1;
    if(l1 == NULL && l2 == NULL)
    return NULL;
    
    struct ListNode* newList = NULL;
    struct ListNode *tail = newList;
	//取下最小值做新鏈表的頭
    if(l1->val < l2->val)
    {
        newList = tail = l1;
        l1 = l1->next;
    }
    else
    {
        newList = tail = l2;
        l2 = l2->next;
    }
	//取兩個鏈表中最小值尾插到新的鏈表中去
    while(l1 && l2)
    {
       if(l1->val < l2->val)
       {
           tail->next = l1;
           l1 = l1->next;
       }
       else
       {
           tail->next = l2;
           l2 = l2->next;
       }
       tail = tail->next;
    }

	//如果有一個鏈表還沒遍歷完就繼續尾插到新鏈表去
    if(l1)
    {
        tail->next = l1;
    }
    if(l2)
    {
        tail->next = l2;
    }

    return newList;
}

帶哨兵位的頭節點不需要考慮新鏈表頭尾都是是NULL的這種情況

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
	//創建哨兵頭節點
    struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = head;
    tail->next = NULL;

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            tail->next = l1;
            l1 = l1->next;
        }
        else
        {
            tail->next = l2;
            l2 = l2->next;
        }
        tail = tail->next;
    }

    //還剩的
    if(l1){
        tail->next = l1;
    }
    if(l2){
        tail->next = l2;
    }
	
	//釋放動態開辟的結點,回傳新的頭
    struct ListNode* node = head;
    head = head->next;
    free(node);
    return head;
}

OR36 鏈表的回文結構

鏈接: 點我 .
在這里插入圖片描述

解題思路:先找出中間結點(快慢指標法),將中間結點起始位置往后整體逆置,回傳逆置后的頭指標,得到后部分逆置的鏈表,再跟原鏈比較

class PalindromeList {
public:
    struct ListNode* middleNode(struct ListNode* head){
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
       
        fast = fast->next->next;
       
    }

    return slow;
}
    struct ListNode* reverseList(struct ListNode* head){
    if(head == NULL)
    {
        return NULL;
    }
    
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    struct ListNode* next = cur->next;

    while(cur)
    {
       //逆置
       cur->next = prev;
       //迭代
       prev = cur;
       cur = next;
       if(next != NULL)
       next = next->next;
    }

    return prev;
}
    bool chkPalindrome(ListNode* A) {
        // write code here
        //找到中間結點整體逆置,最后比較
        struct ListNode* mid = middleNode(A);
        struct ListNode* rhead = reverseList(mid);
        
        //比較
        while(A && rhead)
        {
            if(A->val != rhead->val)
            {
                return false;
            }
            else
            {
                A = A->next;
                rhead = rhead->next;
            }
        }
        return true;
    }
};

在這里插入圖片描述
160. 相交鏈表
鏈接: 點我.

鏈表的相交并不是直線的相交,而是兩個鏈表中存在一個公共結點,這樣的鏈表我們可以稱它是相交鏈表,比較結點的地址,如果相同那么就是相交的,否則不是,讓長距離的先走差距步,最后再同時走,如果中間相交就回傳該結點,否則回傳NULL

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //記錄鏈表的長度
    int lenA = 0,lenB = 0;
    struct ListNode *curA = headA;
    struct ListNode *curB = headB;
    while(curA->next)
    {
        lenA++;
        curA = curA->next;
    }
    while(curB->next)
    {
        lenB++;
        curB = curB->next;
    }
    //如果不相遇那么鏈表的最后一個結點一定不同
    if(curA != curB)
    return NULL;
    //定義長的鏈表,定義短的鏈表
    struct ListNode * longlist = headA, *shortlist = headB;
    int gap = abs(lenB - lenA);
    if(lenA < lenB)
    {
        longlist = headB;
        shortlist = headA;
    }
    //長鏈表先走gap步
    while(gap--)
    {
        longlist = longlist->next;
    }
    //最后一起走
    while(longlist != shortlist)
    {
        longlist = longlist->next;
        shortlist = shortlist->next;
    }

    return longlist;
}

141. 環形鏈表

鏈接: 點我.
在這里插入圖片描述

快慢指標解法,快指標走兩步,慢指標走一步,當他們相遇了,就能證明鏈表是環形鏈表,如果fast走到NULL的位置或者fast的下一個結點是空了,那么鏈表就不為環形

bool hasCycle(struct ListNode *head) {
    struct ListNode * fast = head;
    struct ListNode * slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}

延申問題

  • 1、為什么slow走一步,fast走兩步,它們一定會在環里面相遇,會不會永遠追不上,請證明

  • 2、slow走一步,fast走3步?走4步?走n步行不行?請證明!

  • 3、求環的入口點

在這里插入圖片描述
不會,假設slow進環的時候fast跟slow的差距是N, 緊接著追擊的程序,fast往前走2步,slow走1步,它們每次一走,它們之間的距離縮小1,N-1 N-2 … 2 1 0,直到等于0的時候就相遇了

2、slow走一步,fast走3步?走4步?走n步行不行?請證明!
假設slow進環的時候fast跟slow的差距是N, 緊接著追擊的程序,fast往前走3步,slow走1步,它們每次一走,它們之間的距離縮小2,會分兩種情況,如果是奇數個,如果是偶數個呢?如果是偶數個可以相遇,如果是奇數(-1)個并不會相遇,假設環的大小是C,那么slow和falst的差距就是C-1了,如果C-1恰好是奇數就永遠追不上
在這里插入圖片描述
總結:如果slow進環時,slow跟fast的差距是奇數,且環的長度是偶數,那么它們兩個再環里面就會一直打圈

解題思路:
將fast和slow的相遇點找出來,meet表示相遇點,從相遇點meet開始
head走一步meet走一步,如果他們相遇了,那么這就是入口點

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode * fast = head;
    struct ListNode * slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        //相遇
        if(fast == slow)
        {
             //相遇后,meet從相遇點開始走,head從起點開始走,
            struct ListNode *meet = fast;
            while(head != meet)
            {
                head = head->next;
                meet = meet->next;
            }
            //當head和meet相遇了,這就是入口點
            if(meet == head)
            {
                return meet;
            }
        }   
    }

    return NULL;
}

138. 復制帶隨機指標的鏈表:

點我.
在這里插入圖片描述

解題思路:
要想復制一份鏈表比較得保證新鏈表的結點關系和原鏈表保持一致,并且新鏈表的每個結點的值以及結點中兩個指標的指向都必須與原鏈表結點一樣,將拷貝鏈表的結構構建出來后,同步拷貝鏈表和原鏈表兩個指標的指向,最后將拷貝鏈表拆解下來,同時還原原鏈表結點的鏈接的關系,最后回傳拷貝鏈表的頭

在這里插入圖片描述

struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct Node* cur = head;
    //建立拷貝結點的關系
	while(cur)
    {
       struct Node* next = cur->next;
       struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
       copy->val = cur->val;
       copy->next = next;
       cur->next = copy;
       cur = next;
    }
    //同步拷貝結點的隨機指標
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        if(cur->random == NULL)
        {   
            copy->random = NULL; 
        }
        else
        {
            copy->random = cur->random->next;
        }
        cur = next;
    }
    //創建哨兵位頭節點,新鏈表的尾結點
    cur = head;
    struct Node* copyhead,*copytail;
    copyhead = copytail = (struct Node*)malloc(sizeof(struct Node));
    while(cur)
    {
        //復原新鏈表的關系
        struct Node* copy = cur->next;
        struct Node* next = copy->next;

        copytail->next = copy;
        copytail = copytail->next;
        cur->next = next;

        cur = next;
    }
    //釋放頭結點
    struct Node* guard = copyhead;
    copyhead = copyhead->next;
    free(guard);
    return copyhead;
}

147. 對鏈表進行插入排序

鏈接: 點我.
在這里插入圖片描述
在這里插入圖片描述

解題思路:
取下原鏈表中的第一個結點將他當成一個有序的新鏈表看待,再比較剩余結點構成的鏈表,比較完后將舊鏈表中剩余的結點按照順序方式插入到新的鏈表中

struct ListNode* insertionSortList(struct ListNode* head){
    if(head == NULL || head->next == NULL)
    {
        return head;
    }
    struct ListNode* cur = head->next;
    struct ListNode* rhead = head;
    rhead->next = NULL;
    while(cur)
    {
        struct ListNode* p = NULL;
        struct ListNode* c = rhead;
        struct ListNode* next = cur->next;
       
        while(c)
        {
           if(cur->val < c->val)
           {
               break;
           }
           else
           {
               p = c;
               c = c->next;
           }
        }
        //頭插
        if(p == NULL)
        {
            cur->next = c;
            rhead = cur;
          
        }
        //順序位置處插入
        else
        {
            p->next = cur;
            cur->next = c;
        }
        cur = next;
    }
    return rhead;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/307357.html

標籤:其他

上一篇:PCI、PCIe、Mini PCIe、SATA、mSATA、M.2

下一篇:??強烈推薦!Word、Excel、PPT、PDF在線預覽解決方案

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more