Data structure----structure--linear structure--chain storage--linked list

Data structure ---- structure - linear structure - chain storage - linked list

1. The characteristics of the linked list

The space can be discontinuous and the length is not fixed, which is more flexible than arrays

search:

Time complexity O(n)

additions and deletions:

The time complexity of header addition and deletion is O(1)

Other time complexity is O(n)

Extension: Characteristics of one-way circular linked list

Starting from any node, the entire linked list can be traversed

2. The composition of the linked list

A linked list consists of a data field and a pointer field

3. Realization of linked list and its functions

1. Create a linked list and view the values ​​stored in each linked list

//在Visual Studio 2022编译器下的用c语言的写法
#include <stdio.h>
#include<stdlib.h>

typedef struct Node {
    
    
	int nValue;
	struct Node* pNext;
	
}List;

List* list() {
    
    
	List* m_phead = NULL;
	List* m_tail = NULL;
	int len;
	printf("请输入链表长度\n");
	scanf_s("%d", &len);
	while (len) {
    
    
		List* PTemp=(List*)malloc(sizeof(List));
		printf("请输入数据\n");
		int value;
		scanf_s("%d", &value);
		PTemp->nValue = value;
		PTemp->pNext = NULL;
		if (m_phead) {
    
    //不是空节点
			m_tail->pNext = PTemp;
		}
		else {
    
    //是空节点
			m_phead = PTemp;
		}
         m_tail = PTemp;
		len--;
	}
	return m_phead;
}

void ShowList(List* m_phead) {
    
    
	while (m_phead) {
    
    
		printf("%d  ", m_phead->nValue);
		m_phead = m_phead->pNext;
	}
}

int main() {
    
    
	List* list1=list();
	ShowList(list1);
	printf("\n");
	ShowList(list1);
	return 0;
}

Think about how to print the linked list in reverse without breaking the original linked list structure

method:

1. Violent time complexity O(n square) space complexity O(1)

2. Exchange time complexity O (square of n) space complexity O (see how to use exchange to determine)

3. Stack time complexity O(n) space complexity O(n)

4. The head insertion method creates a new linked list time complexity O(n) space complexity O(n)

5. Array time complexity O(n) space complexity O(n)

6. Recursive time complexity O(n) space complexity O(n)

Here it is implemented recursively

//此函数的定义及其实现依赖于上面的链表代码
void ReserveList(List* head) {
    
    
	if (head->pNext == NULL) {
    
    //如果到了最后一个节点
		printf("%d  ", head->nValue);//打印该节点
		return;//返回
	}
	ReserveList(head->pNext);//先处理下一个
	printf("%d  ", head->nValue);//打印当前节点
}

2. Reverse the linked list (using a method that does not consume space)

Ways to consume space:

1. stack

2. Array

3. Recursion

4. Head interpolation creates a new linked list

method that does not consume space

Implemented with three pointers

1. Record the three pointers respectively as the head, take and break

2. Processing: (1) Insert: change the node next to the node pointed by the pointer to the node pointed by the head pointer

(2) Change the mark: the node pointed to by the pointer of the head becomes the node pointed to by the pointer

​ The node pointed to by the taken pointer becomes the node pointed to by the broken pointer

​ The node pointed to by the broken pointer is the next node of the node pointed to by the broken pointer

Code
//此函数的定义及其实现依赖于上面的链表代码
List* FanZhuan(List* P_head) {
    
    

	if (P_head == NULL || P_head->pNext == NULL) return P_head;
	List* NewHead = NULL;
	List* Na = P_head;
	List* Duan = P_head->pNext;
	
	while (Duan) {
    
    
		Na->pNext = NewHead;
		NewHead = Na;
		Na = Duan;
		Duan = Duan->pNext;
	}
	Na->pNext = NewHead;
	return Na;
}

3. Merge the two linked lists and sort them according to the size of the data in the linked list

method:

1. Define two pointers, one to point to the new header after confirming the new header (the pointer will be operated on later), and one to return after pointing to the new header

2. Processing: compare the two linked lists with the two pointers passed in, then add the tail to the new linked list, and then move the corresponding pointer to the next node until one of the two pointers points to empty to end the loop

3. Connect the remaining linked list with the tail of the new linked list

Code

//此函数的定义及其实现依赖于上面的链表代码
List* HeBing(List* list1_head, List* list2_head) {
    
    
	if (!list1_head) {
    
    
		return list2_head;
	}
	if (!list2_head) {
    
    
		return list1_head;
	}

	List* newHead = NULL;
	List* HEAD = NULL;

	if (list1_head->nValue < list2_head->nValue) {
    
    //确定表头
		newHead = list1_head;
		HEAD = newHead;
		list1_head=list1_head->pNext;

	}
	else {
    
    
		newHead = list2_head;
		HEAD = newHead;
		list2_head = list2_head->pNext;
	}

	while (list1_head && list2_head) {
    
    //循环判断拼接链表
		if (list1_head->nValue < list2_head->nValue) {
    
    
			newHead->pNext = list1_head;
			newHead = newHead->pNext;
			list1_head = list1_head->pNext;
		}
		else {
    
    
			newHead->pNext = list2_head;
			newHead = newHead->pNext;
			list2_head = list2_head->pNext;
		}
	}

	if (list1_head) {
    
    
		newHead->pNext = list1_head;
	}
	if (list2_head) {
    
    
		newHead->pNext = list2_head;
	}
	return HEAD;
}

4. Exercises on linked list topics

The first question (URL is https://leetcode.cn/problems/LGjMqU/)

topic:

Given Lthe head node of a singly linked list head, the singly linked list Lis expressed as:

L0 → L1 → … → Ln-1 → Ln
Please rearrange them into:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 →

You can't just change the value inside the node, but you need to actually exchange the node.

solve:

method one:

violence (don't use this)

Method Two:

Step 1: Split the linked list into two linked lists from the middle (if the length is not even, the former linked list is one node longer than the latter one)

Step 2: Flip the second linked list

Step 3: Merge the two linked lists, the method of merging is first the nodes of the first linked list, then the nodes of the second linked list, and so on

code show as below
//这里的代码是c++语言下的
class Solution {
public:
    void reorderList(ListNode* head) {
        if(head->next==nullptr) return;
        int listsize=0;
        ListNode* headsize=head;
        while(headsize){//判断链表长度
            listsize++;
            headsize=headsize->next;
        }
        if(listsize==2){//如果链表长度为2,结束
            return;
        }
        headsize=nullptr;
        //将链表分成两个    
        int listsize2=listsize/2; //链表2长度
        int listsize1=listsize-listsize2;//链表1长度
        ListNode* Temp=nullptr;
        ListNode* head1=head;//链表一头节点
        ListNode* head2=head;//链表二

        if(listsize1==1){//将链表一与链表二断开 1
            Temp=head1;
        }
        while(listsize1--){//遍历获得链表二头节点
            head2=head2->next;
            if(listsize1==1){
                Temp=head2;//记录链表一的尾节点   
            }
        }
         Temp->next=nullptr;//将链表一与链表二断开 2
        Temp=nullptr;
        ListNode* Temp1=nullptr;
        ListNode* Temp2=nullptr;
       //将链表二进行翻转
       //链表长度大于1进行翻转
       if(listsize2>1){
            ListNode*NewlistHead=nullptr;
            ListNode*Na=head2;
            ListNode*Duan=head2->next;
            while(Duan){
                Na->next=NewlistHead;
                NewlistHead=Na;
                Na=Duan;
                Duan=Duan->next;
            }
            Na->next=NewlistHead;
            //将两链表进行拼接 第一种
                Temp1=head1;
                Temp2=Na;
       }
       else{//不大于1
        //将两链表进行拼接 第二种
            Temp1=head1;
            Temp2=head2;
       }
        while(1){
            ListNode* Temp3=Temp1->next;
            
            Temp1->next=Temp2;
            Temp1=Temp3;
           if(Temp2==nullptr||Temp1==nullptr){
              break;
            }

            ListNode* Temp4=Temp2->next;
            Temp2->next=Temp1;
            Temp2=Temp4;
             
        }
    }
};

The second problem (URL is https://leetcode.cn/problems/3u1WK4/)

topic:

Given the head nodes headAand of two singly linked lists headB, find and return the starting node where the two singly linked lists intersect. If there is no intersection between the two linked lists, return null.

solve:

method one:

Violence (too much time and space to use without this)

Method Two:

Stack (when two linked lists are popped out of the stack, compare them to see if they are the same)

Method three:

Difference method:

​ Step 1: traverse the two linked lists to get the length of the two linked lists

​ Step 2: Subtract to obtain the length difference

​ Step 3: The longer one walks the distance of the length difference first, and then the two pointers to the linked list walk together,

​ If the two pointers point to the same node, the end is found.

​ If the two linked lists point to empty addresses and have not been found, the end has not been found.

Here use method three to write, the code of method three is as follows
//这里的代码是c++语言下的
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int headAsize=0;
        int headBsize=0;
        ListNode*headA1=headA;
        ListNode*headB1=headB;
        int x=0;
        while(headA1){
            headAsize++;
            headA1=headA1->next;
        }
        while(headB1){
            headBsize++;
            headB1=headB1->next;
        }
        headA1=headA;
        headB1=headB;
       if(headAsize>=headBsize){
           x=headAsize-headBsize;
            while(x--){
                headA1=headA1->next;
            }
       }
       else{
            x=headBsize-headAsize;
              while(x--){
                headB1=headB1->next;
            }
       }
       while(1){
            if(headA1==headB1) return headA1;
           if(headA1==nullptr||headB1==nullptr) return 0;
           headA1=headA1->next;
           headB1=headB1->next;
       }
    }
};

Question 3 (https://leetcode.cn/problems/c32eOV/)

topic:

Given a linked list, return the first node where the linked list starts entering the ring. Starting from the head node of the linked list and following nextthe pointer into the ring, the first node is the entry node of the ring. Returns if the linked list is acyclic null.

To represent a cycle in a given list, we use integers posto denote where in the list the tail of the list joins (indexes start at 0). If posyes -1, there are no cycles in the linked list. Note that posit is only used to identify the ring and will not be passed as a parameter to the function.

**Description:** It is not allowed to modify the given linked list.

solve:
method one

Fast and slow pointer:
the first step: use the fast and slow pointer to find the intersection point (if no node is found, it is acyclic)

​ Step 2: Disconnect the intersection point (to be restored later)

Step 3: Define two pointers starting from the intersection point and the starting point, and traverse to obtain the length

​ Step 4: Subtract to obtain the length difference

Step 5: The longer one walks the distance of the length difference first, and then the two pointers to the linked list walk together,

​ When two pointers point to the same node, return that node.

Method Two:

Flip the linked list, and store the linked list that is to be flipped each time (using a pointer array), and then traverse the head and tail of the array in the middle of the stored pointer array. When you find a different one for the first time node, the node that returned last time is the loop-entry point

Method three:

Mathematical derivation:

insert image description here

​ After derivation, a=(R-1)(b+c)+c

​ Therefore, as long as two pointers are used to point to point A and point C and start at the same speed at the same time, they will meet at point B in the end, and the loop entry point can be obtained

Here use method one to write, the code of method one is as follows
//这里的代码是c++语言下的
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==nullptr){//如果没节点返回空,无环
            return head;
        }
        if(head->next==nullptr){//如果只有一个节点返回空,无环
            return 0;
        }
        //定义快慢指针
        ListNode *Fast=head;
        ListNode *Slow=head;
        //定义一个指针指向交点
        ListNode *JiaoDian=nullptr;
        ListNode *JiaoDian2=nullptr;
        //快慢指针进行遍历找到交点
        while(1){
            if(Fast->next==nullptr){//如果快指针会指向为空,无环
                return Fast->next;
            }
            else if(Fast->next->next==nullptr){
                return Fast->next->next;
            }
            
            Fast=Fast->next->next;
            Slow=Slow->next;
            if(Fast==Slow){//找到交点
                JiaoDian=Fast;
                JiaoDian2=Slow=Slow->next;//断点的下一个
                Fast->next=nullptr;//将交点断开,一会要还原
                break;
            }
        }
        //定义从交点和起始点开始的指针
        ListNode *StartJiao=Slow;
        ListNode *Start=head;
        //遍历两个指针获得长度
        int StartJiaolen=0;
        int Startlen=0;
        while(StartJiao){//起点开始的长度
            StartJiaolen++;
            StartJiao=StartJiao->next;
        }
        while(Start){//断点开始的长度
            Startlen++;
            Start=Start->next;
        }
        StartJiao=Slow;//回到起点
        Start=head;
        int chalen=0;//相差长度
        if(StartJiaolen>Startlen){
            chalen=StartJiaolen-Startlen;
            while(chalen--){//移动交点指针
                StartJiao=StartJiao->next;
            }
        }
        else{
            chalen=Startlen-StartJiaolen;
            while(chalen--){//移动开始指针
                Start=Start->next;
            }
            
        }

        while(1){//移动找到入口点
            if(Start==StartJiao){
                JiaoDian->next=JiaoDian2;
                return Start;
            }
            if(Start->next==nullptr||StartJiao->next==nullptr){
                return 0;
            }
           Start=Start->next;
           StartJiao=StartJiao->next;
           
        }
    }
};

Question 4 (https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/)

topic:

Please implement copyRandomListthe function to copy a complex linked list. In a complex linked list, each node not only has a nextpointer pointing to the next node, but also a randompointer pointing to any node in the linked list or null.

solve:
method one:

​ Violent time complexity is O(n square), it takes too long, so don’t use this method

Method Two:

Step 1: Create a new linked list (implements the next pointer)

Step 2: Merge the two linked lists cross-merging

Step 3: Implementation (random pointer)

Step 4: Restore the two linked lists separately

Step 5: Return to the new linked list

The time complexity is O(n) and the space complexity is O(1)

code show as below
//这里的代码是c++语言下的
class Solution {
public:
  
    Node* copyRandomList(Node* head) {
        if(head==nullptr){
            return head;
        }
        
        Node* BianLi=head;//用于遍历原来链表的指针
        Node* NewHead=nullptr;//新链表
        Node* Temp=nullptr;//用来指向新链表的表头
     
        //添加完成新链表(random指针还没有实现)

        NewHead=Temp=NewNode(*BianLi);//表头
        BianLi=BianLi->next;
        while(BianLi){
            Node* Temp2=NewNode(*BianLi);
            BianLi=BianLi->next;
            Temp->next=Temp2;
            Temp=Temp->next;
           
        }
       
        //将两个链表相交连接
        Temp=NewHead;//重置  用于遍历新链表的指针
        BianLi=head;//重置  用于遍历原来链表的指针
        while(1){
           
            Node* LinShi1=BianLi->next;//临时变量
            BianLi->next=Temp;
            BianLi=LinShi1;
            if(BianLi==nullptr){
                break;
            }
            
            Node* LinShi2=Temp->next;//临时变量
            Temp->next=BianLi;
            Temp= LinShi2;
        }

        //将random指针实现
        Node* Temp3=head;//用于遍历合并后两个链表的指针
        int bool1=1;//判断是不是原有链表的节点
        while(1){//循环将复制的节点的复杂指针实现

            if(bool1%2==1){
                if(Temp3==nullptr){//结束条件
                    break;
                }
                if(Temp3->random==nullptr){//如果指向为空,那对应的就给空,这里是因为我合并的那个链表只有一个nullptr
                    Temp3->next->random=nullptr;
                }
                else{
                     Temp3->next->random=Temp3->random->next;
                }
               
            }

            Temp3=Temp3->next;
           
            bool1+=1;
        }

        //将两个链表进行拆分
        Temp3=head;//重置 用于遍历合并后两个链表的指针
        while(1){
            if(Temp3==nullptr){//终止条件
                break;
            }
            Node* LinShi1=Temp3->next;//临时变量
            if(Temp3->next==nullptr||Temp3->next->next==nullptr){//最后两个数据的处理
                Temp3->next=nullptr;
            }
            else{
                Temp3->next=Temp3->next->next;
            }
             Temp3=LinShi1;
            
        }
        return NewHead;
    }

    //造新节点
     Node* NewNode(Node node){
        Node*newnode=(Node*)malloc(sizeof(Node));
        newnode->val=node.val;
        newnode->next=nullptr;
        newnode->random=nullptr;
        return newnode;
    }
};

5. How to judge whether a number is an integer power of 2 (this question has nothing to do with linked lists)

The integer is n, use n&(n-1) to see if it is equal to 0, equal to 0 is the integer power of 2

6. Find a bit of a number that is 1 in binary (randomly find a bit that is 1) (this question has nothing to do with linked lists)

The integer is n, you can find it with n&(-n)

7. Jump list: SkipList (not a linked list structure)

Jump lists are based on ordered linked lists

1. Implementation of jump table lookup

1. According to the length of the linked list, determine how many layers there are = the length of the linked list of log2

2. Which elements in each layer are determined according to the probability (the probability of each element is 1/2)

3. Then find the number from the high level to the low level. When comparing each level with the elements in the linked list, take the right side if it is greater than the element, and take the left side if it is less than the value. If it is equal, you will find it (this step is similar to dichotomy)

4. Go to the last layer to see if you find it

2. Implementation of adding jump table

1. According to the length of the linked list, determine how many layers there are = the length of the linked list of log2

2. Which elements in each layer are determined according to the probability (the probability of each element is 1/2)

3. Then find the element to be added in order from the high level to the low level, and save it at the place where it should be inserted in each layer

4. Judge whether each layer has this element according to the probability (probability is 1/2)

5. Insert

8. Hash table (hash table): hashTable

1. Determine the grouping

The formula of using the integer and remainder method is: P=key%M (M is the number) (there will be a hash conflict problem)

2. Determine the hash conflict solution

1. Open addressing method:

1. Linear detection

2. Second detection

2. Zipper method:

​ Step 1: Define a linked list structure

Step 2: Apply for an array of pointers (the initial value of each element in the array is empty)

Step 3: Elements into groups (head interpolation method)

​ Step 4: Find

The code to implement a simple hash table using the zipper method is as follows (this code is written in c language)

#include <stdio.h>
#include<stdlib.h>
#include<windows.h>
typedef struct Node {
    
    
	int nValue;
	struct Node* pNext;
	                                                                                
}List;

List* list() {
    
    
	List* m_phead = NULL;
	int len = 1;
	int date;
	printf("请输入数据\n");
	scanf_s("%d", &date);
	
	m_phead = (List*)malloc(sizeof(List));
	m_phead->nValue = date;
	m_phead->pNext = NULL;
	return m_phead;
}

//申请指针数组
List** Array(int n) {
    
    
	List** array_head = (List**)(malloc(sizeof(List*) * n));
	memset(array_head, 0, (sizeof(List*) * n));
	return array_head;
}
//头插法
void pushhead(List* lst, List** arr) {
    
    
	lst->pNext = (*arr);
	*arr = lst;
}

//元素入组
void TianJia(int x,int n, List* lst, List** arr) {
    
    

	int weiyi = x % n;
	List** temp = arr;
	while (weiyi--) {
    
    
		temp++;
	}
	if (*temp) {
    
    
		pushhead(lst, temp);
	}
	else {
    
    
		*temp = lst;
	}
}



void ShowList(List* m_phead, int x) {
    
    
	while (m_phead) {
    
    
		if (m_phead->nValue == x) {
    
    
			printf("找到了");
			printf("%d", x);
			return;
		}
		m_phead = m_phead->pNext;
	}
	printf("没找到");
}
//查找
void find(int x, int n, List** arr) {
    
    
	int y = x % n;
	arr += y;
	ShowList(*arr, x);
}


int main() {
    
    
	int n = 5;
	//申请指针数组
	List** arr=Array(n);
	List* lsti1 = list();
	TianJia(lsti1->nValue,n, lsti1, arr);//元素入组,
	List* lsti2 = list();
	TianJia(lsti2->nValue, n, lsti2, arr);//元素入组
	List* lsti3 = list();
	TianJia(lsti3->nValue, n, lsti3, arr);//元素入组
	List* lsti4 = list();
	TianJia(lsti4->nValue, n, lsti4, arr);//元素入组
	List* lsti5 = list();
	TianJia(lsti5->nValue, n, lsti5, arr);//元素入组
	find(2, n, arr);//查找元素
	return 0;
}

3. Optimization of linear detection

Loading factor α=element/table length<0.8, the closer it is to 0.8, the higher the possibility of conflict

The optimization method is to apply for a larger space and try to make the load factor less than 0.8

4. The respective advantages of zipper method and linear detection optimization

Zipper method:

1. Handling conflicts is simple

2. It is easy to delete data

3. Applicable to the situation where the number of elements is unknown

​ 4. Deal with the situation where elements occupy a large and large space, and use less space

Linear probing optimization:

​ Handle the situation where elements occupy a small and small space, and use less space

Guess you like

Origin blog.csdn.net/m0_73483024/article/details/132178436