Data structure and algorithm 1 - linked list

Posted by blacksnday on Mon, 03 Jan 2022 22:55:57 +0100

Data structure and algorithm - linked list

Previous contents
2-stack
3-queue
4-tree
5-fig
6-greedy algorithm
7-recursion and divide and conquer
8-sort
9 - query
10 - dynamic programming
11-STL Library

Basic concepts

Linear table: a finite sequence of zero or more elements

1, Storage structure of linear table

There are two storage structures of linear table: linear sequential structure and chain storage structure

1.1 sequential storage structure

//Define sequential storage linear tables
#define MAXSIZE 20 / / define the maximum number of elements
typedef int ElemType;//Define the type of data
struct SqList
{
ElemType data[MAXSIZE];
int length;//Defines the current length of the linear table
};

1.2 chain storage structure

//Storage structure of single linked list
typedef int ElemType;
struct Node
{
ElemType data;//Data domain
Node *Next;//Pointer field
};
//Defines the pointer to the node

typedef int ElemType;
typedef struct  DulNode
{
ElemType data;
DulNode *prior;//Pointer to precursor
DulNode *next;//Pointer to successor

2, Basic operation

2.1 getting elements

Sequential storage structure (be sure to pay attention to boundary conditions)

/*
Get element operation: bool GetElem(SqList L,int i,ElemType *e)
Function: in the linear table L, transfer the value of the ith position to e
Returns True if the delivery is successful and False if it fails
*/
bool GetElem(SqList L,int i,ElemType *e)
{
//Prevention of cross-border
if(L.length==0 || L.length>MAXSIZE || i<1)
{
return false;
}
*e=L.data[i-1];
return true;
}

Chain storage structure (pay attention to the second judgment condition)

/*
Get element operation: bool GetElem(LinkList L,int i,ElemType *e)
Function: in the linear table L, transfer the value of the ith position to e
Returns True if the delivery is successful and False if it fails
*/
{
//Suppose there is a header node
LinkList p=L->Next;//p points to the first node
int j=1;
while(p && j<i)
{
p=p->Next;
++j;
}
if(!p || j>i)//Cross border: 1 The linked list is empty, 2 The value of I is less than 0
{
return false;
}
*e=p->data;//Gets the value of the ith position
return true;
}

2.2 insertion operation

Sequential storage

/*
Insert operation: bool ListInsert(SqList *L,int i,ElemType e)
Function: insert the value e at position i of linear table L, and return the bool type flag to judge whether the insertion is successful
Assume that all elements move backward from the insertion point
*/
bool ListInsert(SqList *L,int i,ElemType e){
//Judge whether it is out of bounds
if(L->length==MAXSIZE)//Judge whether the sequential linear table is full
{
return false;
}
if(i<1|| i>L->length+1)
{
return false;
}
if(i<L->length)//If it is not at the end of the table, other elements need to vacate their current position
{
for(int k=L->length-1;k>i-1;k--)//From the value of the current position k, move backward in turn
{
L->data[k+1]=L->data[k];//To start with the last digit at the end, borrow and move forward from the back. Wrong writing method: L - > data [k] = L - > data [k-1];
}
}
L->data[i-1]=e;
L->length++;
return true;
}

/*
Insert operation: bool ListInsert(LinkList *L,int i,ElemType e)
Function: insert the value e at position i of linear table L, and return the bool type flag to judge whether the insertion is successful
Assume that all elements move backward from the insertion point
*/
p=*L;
int j=1;
while(p && j<i)
{
p=p->Next;
++j;
}
if (!p || j>i)
{
return false;
}//p points to the position of i-1

//Request memory space
s->data=e;

s->Next=p->Next;
p->Next=s;

return true;
}

2.3 deleting

Sequential storage (elements need to be moved in all locations except the last location)

/*
Delete operation: bool LisDelete(SqList *L,int i,ElemType *e)
Function: delete the i-th digit in the L-order linear table, store the deleted value in (* e), and return the bool type flag to judge whether the deletion is successful
*/
bool LisDelete(SqList *L,int i,ElemType *e)
{

if(L->length==0)//Remember, don't forget this condition
{
return false;
}
if(i<1 && i>L->length)
{
return false;
}
*e=L->data[i-1];
if(i<L->length)
{
for(int k=i-1;k<L->length;k++)
{
L->data[k]=L->data[k+1];
}
}
L->length--;
return false;
}

/*
Delete operation: bool LisDelete(LinkList *L,int i,ElemType *e)
Function: in the ith digit of L linear table, delete the element, store the deleted value in (* e), and return the bool type flag to judge whether the deletion is successful
*/
{

int j=1;
while(p && j<i)
{
p=p->Next;
j++;
}
if(!(p) || j>i)
{
return false;
}//Point to the i-1 element

s=p->Next;
p->Next=s->Next;
*e=s->data;

free(s);//Reclaim free memory

return true;
}

The head interpolation method is relatively simple, and the tail interpolation method requires additional pointers to record the tail nodes

//Head node pointer: the node with empty data field: first generate the node with malloc function, and then define the next pointer field as empty, without assigning value to the data.
/*
Creation of single linked list and whole table 1: header insertion method
Randomly generate the values of n elements and establish a single linear linked list L
n Represents the length of the linear table
*/
{
int i;

//Define a random number seed
//srand(time(0));

for(int i=0;i<n;i++)
{
//Create node and apply for memory
p->data=i;//input data

p->Next=(*L)->Next;//The current node refers to the original node of the head node, which is equivalent to inserting the current node between the head node and the original node
(*L)->Next=p;//The header points to the current node
}
}

//First, use malloc function to generate a header, execute the node link of the loop in the middle, and point the next pointer of the tail node to null in the last sentence of the function
/*
Creation of single linked list and whole table 2: tail interpolation
Randomly generate the values of n elements and establish a single linear linked list L
n Represents the length of the linear table
*/
{
int i;

//Define a random number seed
//srand(time(0));
//(*L)->Next=NULL;// A single node with an empty list header is created first

p=*L;

for(int i=0;i<n;i++)
{
//Create node and apply for memory
s->data=i;//input data

p->Next=s;
//s->Next=NULL;
p=s;
}

s->Next=NULL;//The tail node is empty
}

2.5 deletion of linked whole table

/*
Delete the whole table of single linked list:
*/
{
p=(*L)->Next;//Point to the first node

//Recycle node pointer
while(p)
{
s=p->Next;
free(p);
p=s;
}

(*L)->Next=NULL;

return true;
}

3, Leetcode brush questions

2.1 construction

structure

struct ListNode{
int val;
ListNode *next;
};

Simple construction method

ListNode a(1);
ListNode b(4);
ListNode c(6);
ListNode d(0);
ListNode e(5);
ListNode f(7);
a.next = &b;
b.next = &c;
d.next = &e;
e.next = &f;
f.next=nullptr;

leetcode 206 - reverse linked list
Idea: Double pointers, one pointing to the head node of the reverse chain (initialized to null, just forming the conversion), and the other recording the next node of the head node. (head pointer, front position of head pointer, next position of head pointer)

class Solution {
public:
//Double pointer
ListNode * next=nullptr;
{
}

}
};

leetcode 92 - reverse linked list
Idea: 1 Note the saving of four location nodes, 2 Note the returned header node 3 Pay attention when using while++

class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode *lpr_ptr=nullptr;
ListNode * rpr_ptr=nullptr;
ListNode * rptr=nullptr;

for(int i=1;i<left;i++)
{
lpr_ptr=lptr;
lptr=lptr->next;
}
rpr_ptr=nullptr;
rptr=lptr;
for(int i=left;i<right+1;i++)
{
ListNode *temp=rptr->next;
rptr->next=rpr_ptr;
rpr_ptr=rptr;
rptr=temp;
}
//Splice data
if(lpr_ptr)//Special case handling
{
lpr_ptr->next=rpr_ptr;
lptr->next=rptr;
}
else
{
lptr->next=rptr;
}

}
};

2.2 and 2.3 summary of two questions: when reversing the linked list, you need to have three pointers to save the address of the current node precursor (initialized as nullptr), the current node address (the default is the header pointer address) and the subsequent address (to avoid loss),

2.4 find the intersection of two linked lists

Idea 1: violence solution, define two vector containers, traverse the linked list a and b respectively, store all nodes in the corresponding container respectively, and then traverse from back to front until unequal nodes are found, and return to the previously saved nodes. (Note: 1. Write ';', 2. Remember to write + + operations in the while loop, otherwise it will lead to an endless loop)

class Solution {
public:
vector<ListNode *> vec_A;
vector<ListNode *> vec_B;

while(p)
{
vec_A.push_back(p);
p=p->next;
}
while(p)
{
vec_B.push_back(p);
p=p->next;
}

int i=vec_A.size()-1;
int j=vec_B.size()-1;
p=nullptr;
while(i>=0 && j>=0 && vec_A[i]==vec_B[j])
{
p=vec_A[i];
i--;
j--;
}
return p;
}
};

Idea 2: first traverse all the nodes in the A-linked list, save all the nodes in the A-linked list in a set set, and then traverse all the nodes in B. each time, check whether the node is in the set set. If the node is returned, Otherwise, nullptr is returned (Note: the operation of adding elements to set is insert, not push_back. In addition, set has its own find method, and vector does not have its own find method).

class Solution {
public:
//Use the set set to find the first identical pointer address
set<ListNode *> node_set;
while(pnode)
{
node_set.insert(pnode);
pnode=pnode->next;
}

while(pnode)
{
if(node_set.find(pnode)!=node_set.end())
{
return pnode;
}
pnode=pnode->next;
}

return nullptr;
}
};

Idea 3: for the common part, you can first align the linked list, that is, first traverse from the longer linked list to the location of the shorter linked list, and then traverse synchronously until the same common location is found. Note: the space complexity is O(1)

class Solution {
int getListLen(ListNode *head)//Find the length of the array
{
int len=0;
{
len++;
}
return len;
}
public:

if(lenA>lenB)
{
for(int i=0;i<lenA-lenB;i++)
{
}
}
else
{
for(int i=0;i<lenB-lenA;i++)
{
}
}

{
}
return NULL;
}
};

Idea 1: the idea of set. Each time you traverse a node, you first judge whether it is in the set. If it is not added to the set, otherwise it is a ring linked list. When you traverse an empty node, you return No.

class Solution {
public:
set<ListNode *> nodeSet;

while(nodePtr)
{
if(nodeSet.find(nodePtr)!=nodeSet.end())//Is there a node in the collection
{
return true;
}
nodeSet.insert(nodePtr);
nodePtr=nodePtr->next;
}
return false;
}
};

Idea 2: the fast and slow pointer points to the head node with two pointers. One takes one step and the other takes two steps. The fast pointer catches up with the slow pointer. It is a circular linked list. The fast pointer goes to the empty node, not a circular linked list

class Solution {
public:

while(fast)
{
fast=fast->next;
slow=slow->next;
if(fast==nullptr)
{
break;
}
fast=fast->next;
if(fast==slow)
return true;
}
return false;
}
};

class Solution {
public:
//Fast and slow pointer method, ferrule method a+b+c+b=2*(a+b) therefore, a=c, starting from points a and c at the same time, the meeting point is the entrance of the ring
ListNode * meet=nullptr;

while(fast)
{
//Go first
fast=fast->next;
slow=slow->next;
if(!fast)
return nullptr;
fast=fast->next;//Come on, take two steps

if(fast==slow)
{
meet=fast;
break;//Remember to jump out of the loop
}
}

{
meet=meet->next;
}

return nullptr;

}
};

Idea: create two empty header pointers, compare the elements of each node, use the tail insertion method, insert the corresponding elements into the two linked lists, splice the data, and set the tail pointer to nullptr, = = (if nodeptr - > next operation is on the left of the equal sign, remember to back up the data pointed to)==

class Solution {
public:
ListNode* partition(ListNode* head, int x) {
//Tail difference method - tail pointer

//Move pointer

while(nodePtr)
{
if(nodePtr->val<x)
{
small_end_ptr->next=nodePtr;//Tail interpolation data
small_end_ptr=nodePtr;
}
else
{
big_end_ptr->next=nodePtr;
big_end_ptr=nodePtr;
}
nodePtr=nodePtr->next;
}
big_end_ptr->next=nullptr;

}
};

2.7 merge two ordered linked lists

Leetcode-24 combines two ordered linked lists
Idea: create an empty head node, traverse the two linked lists respectively and add them to the new linked list. Due to the need to sort from small to large, the tail insertion method needs a new pointer to record the position of the tail node, and finally point the next pointer field of the tail node to NULL;
Clever use of head null pointer

class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {

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

}
};

2.8 merge K ascending linked lists

Leetcode-23 merge K ascending linked lists
Idea: divide the list merging problem in lists into the first half and the second half, and then divide the sub problems. Know that it is divided into the list merging problem, divide and rule it.

class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {//Code for merging linked lists

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

}

ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size()==0)
return nullptr;
if(lists.size()==1)
return lists;
if(lists.size()==2)
return mergeTwoLists(lists,lists);

int mid=lists.size()/2;

vector<ListNode *> sub1_lists;
vector<ListNode *> sub2_lists;

for(int i=0;i<mid;i++)
{
sub1_lists.push_back(lists[i]);
}
for(int i=mid;i<lists.size();i++)
{
sub2_lists.push_back(lists[i]);
}

ListNode *l1=mergeKLists(sub1_lists);//First half: divide and conquer 1
ListNode *l2=mergeKLists(sub2_lists);//Second half: divide and Conquer 2

return mergeTwoLists(l1,l2);
}
};

come on.