Chapter 3: Linked Lists & Arrays

Chapter 3: Linked Lists and Arrays

Table of Contents

1. Linked Lists

A linked list is a data structure used for storing collections of data and has the following properties.

  • Successive elements are connected by pointers. The last element points to NULL.
  • Can grow or shrink in size during execution of a program. Can be made as long as required (until system exhausts).
  • Does not waste memory space (but takes some extra memory for pointers). It allocates memory as list grows.

Advantages:

  • They can be expanded in constant time O(1).

Disadvantages:

  • Access time to individual elements O(n)
  • The overhead with storing an retrieving data

2. Array

One memory block is allocated for the entire array to hold the elements of array. The array elements can be accessed in constant time by using the index of the particular element as subscript. To access an array element, the address of an element is computed as an offset from the base address of the array and one multiplication is needed to compute what is supposed to be added to the base address to get the memory address of the element.

Advantages:

  • Simple and easy to use
  • Faster access to the elements (constant access) O(1)
  • Spacial locality in memory which benefits from modern CPU caching methods.

Disadvantages:

  • Preallocates all needed memory up front and wastes memory space for indices in the array which are empty.
  • Fix size: The size of array is static (specify it before using).
  • One block allocation: may not be possible to get the memory for the complete array if the size is big.
  • Complex position-based insertion: need to shift the existing elements when insertion O(n)

Dynamic Arrays

Dynamic array is a random access, variable-size list data structure that allows elements to be added or removed.


3. Comparison

parameterLinked ListArrayDynamic Arrays
IndexingO(n) traversingO(1)O(1)
Insertion/ deletion at beginningO(1)O(n) shiftingO(n)
Insertion at endingO(n) traversingO(1)O(1),if array is not full; O(n) if array is full
Deletion at endingO(n) traversingO(1)O(n)
Insertion/Deletion in the middleO(n) traversingO(n) shiftingO(n)
Wasted spaceO(n) pointers0O(n)

4. Singly Linked Lists

Singly linked list consists of a number of nodes in which each node has a next pointer to the following element and the last node is NULL which indicates the end of the list.

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

4.1 Traverse

Let’s assume that the head points to the first node of the list.

int ListLength(struct ListNode* head){
	struct ListNode *current = head;
	int count = 0;
	while (current!=NULL){
		count++;
		current = current -> next;
	}
	return count;
}

4.2 Insertion

Inserting a Node at the Beginning
  • Update the next pointer of new node to point to the current head.
  • Update head pointer to point to the new node.
    在这里插入图片描述
Inserting a Node at the Ending
  • New nodes next pointer points to NULL.
    在这里插入图片描述

  • Last nodes next pointer points to the new node.
    在这里插入图片描述

Inserting a Node at the middle
  • If we add an element at position n then we stop at position n-1. Assume that the (n-1)th node called position node. The new node points to the next node of the position where we want to add this node.在这里插入图片描述

  • Position node’s next pointer now points to the new node.
    在这里插入图片描述

Code
void InsertLinkedList(struct ListNode **head, int data, int position){
	int k=1;
	struct ListNode *curr, *prev, *newNode;
	newNode=(ListNode*)malloc(sizeof(struct ListNode));
	if(!newNode){
		printf("Memory Error");
		return;
	}
	newNode->data=data;
	curr=*head;

	/*Inserting at the beginning*/
	if(position==1){
		newNode->next=curr;   	//new node points to the current head
		*head=newNode;			//head points to the new node
	}

	/*Inserting at the certain position*/
	else{
		while((curr!=NULL) && (k<position)){		//Traverse
			k++;
			prev=curr;
			curr=curr->next;	//find the position (between prev and curr) where we watnt to insert
		}
		prev->next=newNode;		//more optimum way
		newNode->next=p;
	}
}

4.3 Deletion

Deleting the First Node
  • Create a temporary node which will point to the same node as that of head.
    在这里插入图片描述
  • Move the head nodes pointer to the next node and dispose of the temporary node.
    在这里插入图片描述
Deleting the Last Node
  • Traverse the list and while traversing maintain the previous node address also.
    在这里插入图片描述
  • Update previous node’s next pointer with NULL.
    在这里插入图片描述
  • Dispose of that tail node.
    在这里插入图片描述
Deleting an Intermediate Node
  • Maintain the previous node while traversing the list. Once we find the node to be deleted, change the previous node’s next pointer to the next pointer of the node to be deleted.
    在这里插入图片描述
  • Dispose of the current node to be deleted.
    在这里插入图片描述
Code
void DeleteNodeFromLinkedList(struct ListNode **head, int position){
	int k=1;
	struct ListNode *prev, *curr;
	if(*head==NULL){
		printf("List Empty");
		return;
	}	
	curr=*head;
	
	/*delete from the beginning*/
	if(position==1){
		*head=(*head)->next;
		free(curr);
		return;
	}

	/*delete a certain position node*/
	else{
		while((curr!=NULL) && (k<position)){
			k++;
			prev=curr;
			curr=curr->next;
		}
		if(curr==NULL)
			printf("position does not exist");
		else{
			prev->next=curr->next;
			free(curr);
		}
	}
}		
Deleting List

This works by storing the current node in some temporary variable and freeing the current node. After freeing the current node, go to the next node with a temporary variable and repeat this process for all nodes.

Code
void DeletLinkedList(struct ListNode **head){
	struct ListNode *auxilaryNode, *iterator;
	iterator=*head;
	while(iterator){
		auxilaryNode=iterator->next;
		free(iterator);
		iterator=auxilaryNode;
	}
	*head=NULL;		//to affect the real head back in the caller.
}

5. Double Linked List

Advantages

  • We can navigate in both directions which means that when deleting node we don’t have to maintain the previous node’s address.

Disadvantages

  • Each node requires an extra pointer, requiring more space.
  • The insertion or deletion of a node need more pointer operation.
struct DLLNode{
	int data;
	struct DLLNode *next;
	struct DLLNode *prev;
};

5.1 Insertion

Inserting a Node at Beginning
  • Update the next pointer of the new node to the current head node (dotted link in the below figure) and also make prev pinterof new nodes as NULL.
    在这里插入图片描述
  • Update head node’s prev pointer to point to the new node and make new node as head.
    在这里插入图片描述
Inserting a Node at the Ending
  • New node next pointer points to NULL and prev pointer points to the end of the list.
    在这里插入图片描述
  • Upate the next pointer of last node to point to new node.
    在这里插入图片描述
Inserting a Node at the Middle
  • New node next pointer points to the next node of the position node where we want to insert the new node. Also new node prev pointer points to the position node.
    在这里插入图片描述
  • Position node next pointer points to the new node and the next node of position node prev pointer points to new node.
    在这里插入图片描述
Code
void DLLInsert(struct DLLNode **head, int data, int position){
	int k=1;
	struct DLLNode *temp, *newNode;
	newNode=(struct DLLNode*)malloc(sizeof(struct DLLNode));
	if(!newNode){
		printf("Memory Error");
		return;
	}
	newNode->data=data;
	
	/*Inserting at the beginning*/
	if(position==1){
		newNode->next=*head;
		newNode->prev=NULL;
		if(*head)
			(*head)->prev=newNode;
		*head=newNode;
		return;
	}

	/*Insert at a certain position*/
	temp=*head;
	while((k<position-1) && temp->next!=NULL){		//traverse
		temp=temp->next;
		k++;
	}
	if(k!=position){
		printf("Desired position does not exist\n");
	}
	newNode->next=temp->next;		//core
	newNode->prev=temp;
	if(temp->next)
		temp->next->prev=newNode;
	temp->next=newNode;
	return;
}

5.2 Deletion

Deleting the First Node
  • Create a temporary node which will point to the same node as that of head.
    在这里插入图片描述
  • Move the head node pointer to the next an change the head’s prev pointer to NULL. Then dispose the temporary node.
    在这里插入图片描述
Deleting the Last Node
  • Traverse the list and while traversing maintain the previous node address also. When we reach the end of the list, we will have two pointers, one pointing to the tail and the other pointing to the node before the tail.
    在这里插入图片描述
  • Update the next pointer of previous node to the tail node with NULL.
    在这里插入图片描述
  • Dispose the tail node.
    在这里插入图片描述
Deleting an Intermediate Node
  • Maintain the previous node while also traversing the list. Upon locating the node to be deleted, change the previous node’s next pointer to the next node of deleted node.
    在这里插入图片描述
  • Dispose of the current node to be deleted.
    在这里插入图片描述
Code
void DDLDelete(struct DLLNode **head, int position){
	struct DLLNode *temp, *temp2;
	temp=*head;
	int k=1;
	if(*head==NULL){
		printf("List is empty");
		return;
	}
	
	/*Delete the first node*/
	if(position==1){
		*head=(*head)->next;
		if(*head!=NULL)
			(*head)->prev=NULL;
		free(temp);
		return;
	}

	/*Delete a certain position node*/
	while((k<position) && (temp->next!=NULL)){			//traverse
		temp=temp->next;
		k++;
	}
	if(k!=position-1){
		printf("Desired position does not exist\n");
	}
	temp2=temp->prev;					//core
	temp2->next=temp->next;
	if(temp->next)
		temp->next->prev=temp2;
	free(temp);
	return;
}	

6. Circular Linked Lists

Circular linked lists do not have ends which means that each node has a successor. Circular linked list are used in managing the computing resource of a computer, for example, when several processes are using the same computer resource (CPU) for the same amount of time, we have to assure that no process accesses the resource before all other processes do (know as Round Robin Algorithm). We can use circular lists for implementing stacks and queues.

typedef struct CLLNode{
	int data;
	struct ListNode *next;
};

在这里插入图片描述

6.1 Traverse

To count the nodes, the list has to be traversed from the acessible node marked head, with the help of a dummy node current, and stop the counting when current reaches the starting node head.

int CircularListLength(struct CLLNode *head){
	struct CLLNode *current=head;
	if(head==NULL)
		return 0;
	int count=0;
	do{ 							
		count++;
		current=current->next;		
	}while(current!=head);
	return count;
}
void PrintCircularListData(struct CLLNode *head){
	struct CLLNode *curr=head;
	if(head==NULL)
		return;
	do{
		printf("%d",curr->data);
		curr=curr->next;
	}while(curr!=head);
}
/*
why we use /do..while/ rather than /while/?
	becuase we judge whether the traverse is finish by the condition (curr==head?) and also we begin from the (curr==head)
	so we cannot only use while loop (which means that we need some modification, eg: begin at (head->next)  
	and do..while loop guarantee that at least the loop body executes one time.
*/

6.2 Insertion

inserting a node at the Front
  • Create a new node and initially keep its next pointer pointing to itself.
    在这里插入图片描述
  • Update the next pointer of the new node with the head node and also traverse the list until the tail. That means in a circular list we should stop at the node which is its previous node in the list (to find the tail).
    在这里插入图片描述
  • Updatae the previous head node in the list to point to the new node.
    在这里插入图片描述
  • Make the new node as the head.
    在这里插入图片描述
Code
void InsertAtBeginInCLL(struct CLLNode **head, int data){
	struct CLLNode *curr=*head;
	struct CLLNode *newNode=(struct CLLNode*)malloc(sizeof(struct CLLNode));
	if(!newNode){
		printf("Memory Error");
		return;
	}
	newNode->data=data;

	while(curr->next!=*head)
		curr=curr->next;
	newNode->next=newNode;
	if(*head==NULL)
		*head=newNode;
	else{
		newNode->next=*head;
		curr->next=newNode;
		*head=newNode;
	}
	return;
}
Inserting a node at the End
  • Create a new node and initially keep its next pointer pointing to itself.
  • Update the next pointer of the new node with the head node and also traverse the list to the tail. Thaat means in a circular list we should stop at the node whose next node is head.
  • Update the next pointer of the previous node to point to the new node.
Code
void InsertAtEndInCLL(struct CLLNode **head, int data){
	struct CLLNode *curr=*head;
	struct CLLNode *newNode=(struct CLLNode*)malloc(sizeof(struct CLLNode));
	if(!newNode){
		printf("Memory Error");
		return;
	}
	newNode->data=data;
	while(curr->next!=*head)
		curr=curr->next;
	newNode->next=newNode;
	if(*head==NULL)
		*head=newNode;
	else{
		newNode->next=*head;
		curr->next=newNode;
	}
}

6.3 Deletion

Deleting the Last Node
  • Traverse the list and find the tail node and its previous node.
    在这里插入图片描述

  • Update the next pointer of tail node’s previous node to point to head.
    在这里插入图片描述

  • Dispose of the tail node.
    在这里插入图片描述

Code
void DeleteLastNodeFromCLL(struct CLLNode **head){
	struct CLLNode *temp=*head, *curr=*head;
	if(*head==NULL){
		printf("List Empty");
		return;
	}
	while(curr->next!=*head){
		temp=curr;
		curr=curr->next;
	}
	temp->next=curr->next;
	free(curr);
	return;
}
Deleting the First Node
  • Find the tail node of the linked list by traversing the list. Tail node is the previous node to the head node which we want to delete.
    在这里插入图片描述

  • Create a temporary node which will point to the head. Also, update the tail node’s next pointer to point to next node of head.
    在这里插入图片描述

  • Move the head pointer to next node. Create a temporary node which will point to head. Also, update the tail nodes next pointer to point to next node of head.
    在这里插入图片描述

Code
void DeleteFrontNodeFromCLL(struct CLLNode **head){
	struct CLLNode *temp=*head;
	struct CLLNode *curr=*head;
	if(*head==NULL){
		printf("List Empty");
		return;
	}
	while(curr->next!=*head)
		curr=curr->next;
	curr->next=*head->next;
	*head=*head->next;
	free(temp);
	return;
}		

7. A Memory-efficient Doubly Linked List

A alternative implementation of doubly linked list ADT with insertion, traversal and deletion operations had been presented. Different from the conventional node definition of doubly linked list which keeps a forward pointer to the next item on the list and a backward pointer to the previous item, the new definition nodes use only one pointer field to traverse the list back and forth.

typedef struct ListNode{
	int data;
	struct ListNode *ptrdiff;
};

The ptrdiff pointer field contains the difference between the pointer to the next node and the pointer to the previous node. The difference is calculated by using exclusive-or operation.

ptrdiff = pionter to previous node ⊕ \oplus pointer to next node

在这里插入图片描述
Let us assume that we are at C node and want to move to D. We konw that C’s ptrdiff is defined as B ⊕ \oplus D. Performing ⊕ \oplus on C’s ptrdiff with B the previous node where we comes from would give D which is the next node. Similarly, by using this operation iteratively, one pointer also allow us to move back and forth. A memory-efficient implementation of a doubly linked list is possible with minimal compromising of time efficiency.


8. Unrolled Linked List

An unrolled linked list stores multiple elements in each block, and each block has a circular linked list which connects all nodes.
在这里插入图片描述

Implementation

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
int blockSize; 			//max num of nodes in one block

struct ListNode{	
	int value;
	struct ListNode* next;
};
struct LinkedBlock{
	int nodeCount;
	struct LinkedBlock *next;
	struct ListNode *head;
};	
struct LinkedBlock *blockHead;

/*create an empty block*/
struct LinkedBlock *newLinkedBlock(){
	struct LinkedBlock *block=(struct LinkedBlock*)malloc(sizeof(struct LinkedBlock));
	block->nodeCount=0;
	block->next=NULL;
	block->head=NULL;
	return block;
}

/*create a node*/
struct ListNode *newListNode(int value){
	struct ListNode *temp=(struct ListNode*)malloc(sizeof(struct ListNode));
	temp->next=NULL;
	temp->value=value;
	return temp;
}

/*search element, time complexity O(n^0.5)*/
void searchElement(int k, struct LinkedBlock **fLinkedBlock, struct ListNode **fListNode){
	//find block
	int j=(k+blockSize-1)/blockSize;		//k-th node is in the j-th block
	struct LinkedBlock *p=blockHead;
	while(--j)
		p=p->next;
	*fLinkedBlock=p;

	//find node
	struct ListNode *q=p->head;
	k%=blockSize;
	if(k==0) k=blockSize;
	k=p->nodeCount+1-k;
	while(k--)
		q=q->next;
	*fListNode=q;
}

/*
start shift operation from block *p 
remove a node from the tail of the circular linked list in a block
and insert a node to the head of the circular linked list in the block after
*/
void shift(struct LinkedBlock *A){
	struct LinkedBlock *B;
	struct ListNode *temp;
	while(A->nodeCount>blockSize){		//if this block still have to shift
		if(A->next==NULL){		//reach the end, a little different
			A->next=newLinkedBlock();
			B=A->next;
			temp=A->head->next;
			A->head->next=A->head->next->next;
			B->head=temp;
			temp->next=temp;
			A->nodeCount--;
			B->nodeCount++;
		}else{
			B=A->next;
			temp=A->head->next;
			A->head->next=A->head->next->next;
			temp->next=B->head->next;
			B->head->next=temp;
			B->head=temp;
			A->nodeCount--;
			B->nodeCount++;
		}
		A=B;
	}
}

void addElement(int k, int x){
	struct ListNode *p, *q;
	struct LinkedBlock *r;
	if(!blockHead){					//initial, first node and block
		blockHead=newLinkedBlock();
		blockHead->head=newListNode(x);
		blockHead->head->next=blockHead->head;
		blockHead->nodeCount++;
	}else{
		if(k==0){					//special case for k=0
			p=blockHead->head;
			q=p->next;
			p->next=newListNode(x);
			p->next->next=q;
			blockHead->head=p->next;
			blockHead->nodeCount++;
			shift(blockHead);
		}else{
			searchElement(k, &r, &p);
			q=p;
			while(q->next!=p)
				q=q->next;
			q->next=newListNode(x);
			q->next->next=p;
			r->nodeCount++;
			shift(r);
		}
	}
}

int searchElement(int k){
	struct ListNode *p;
	struct LinkedBlock *q;
	searchElement(k, &q, &p);
	return p->value;
}

int testUnRolledLinkerList(){
	int tt=clock();
	int m, i, k, x;
	char cmd[10];
	scanf("%d", &m);
	blockSize=(int)(sqrt(m-0.001))+1;

	for(i=0; i<m; i++){
		scanf("%s", cmd);
		if(strcmp(cmd,"add")==0){
			scanf("%d %d", &k, &x);
			addElement(k, x);
		}else if(strcmp(cmd,"search")==0){
			scanf("%d", &k);
			printf("%d\n", searchElement(k));
		}else{
			fprintf(stderr, "Wrong Input\n");
		}
	}
	return 0;
}

9. Skip Lists

Skip list is a data structure that can be used as a probabilistic alternative to balanced binary tree which rearrange the tree as operations are performed to maintain certain balance conditions. As compare to a binary tree, skip lists allow quick search, insertion and deletion of elements. This is achieved by using probabilistic balancing rather than strictly enforce balancing. It is basically a linked list with additional pointers such that intermediate nodes can be skipped.

The find, insert, and remove operations on ordinary binary search trees are efficient, O(logn), when the input data is random; but less efficient, O(n), when the input data is ordered. Skip List performance for these same operations and for any data set is about as good as that of randomly-built binary search trees - namely O(logn).

  • Skip Lists with one Level
    在这里插入图片描述
  • Skip Lists with two Level
    在这里插入图片描述
  • Skip Lists with three Level
    在这里插入图片描述

Implementation

#include<stdio.h>
#include<stdlib.h>
#define MAXSKIPLEVEL 5
struct ListNode{
	int data;
	struct ListNode *next[1];
};
struct SkipList{
	int listLevel;					//current level of list
	struct ListNode *header;
};
struct SkipList list;

/*initialize skip list*/
void initList(){
	int i;
	if((list.header=malloc(sizeof(struct ListNode)+MAXSKIPLEVEL*sizeof(struct ListNode *)))==0){
		printf("Memory Error\n");
		exit(1);
	}
	for(i=0; i<=MAXSKIPLEVEL; i++)
		list.header->next[i]=list.header;
	list.listLevel=0;
}
		
struct ListNode *insertElement(int data){
	int i, newLevel;
	struct ListNode *update[MAXSKIPLEVEL+1];
	struct ListNode *temp;
	temp=list.header;
	for(i=list.listLevel; i>=0; i--){
		while(temp->next[i]!=list.header && temp->next[i]->data<data)
			temp=temp->next[i];
		upadte[i]=temp;
	}
	temp=temp->next[0];
	if(temp!=list.header && temp->data==data)
		return temp;
	
	/*determine level*/
	for(newLevel=0; rand()<RAND_MAX/2 && newLevel<MAXSKIPLEVEL; newLevel++);
	if(newLevel>list.listLevel){
		for(i=list.listLevel+1; i<=newLevel; i++)
			update[i]=list.header;
		list.listLevel=newLevel;
	}
	
	/*make new node*/
	if((temp=malloc(sizeof(Node)+newLevel*sizeof(Node *)))==0){
		printf("insufficient memory(insertElement)\n");
		exit(1);
	}
	temp->data=data;
	for(i=0; i<=newLevel; i++){
		temp->next[i]=update[i]->next[i];
		update[i]->next[i]=temp;
	}
	return temp;
}

void deleteElement(int data){
	int i;
	struct ListNode *update[MAXSKIPLEVEL+1], *temp;
	temp=list.header;
	for(i=list.listLevel; i>=0; i--){
		while(temp->next[i]!=list.header && temp->next[i]->data<data)
			temp=temp->next[i];
		update[i]=temp;
	}
	temp=temp->next[0];
	if(temp==list.header || !(temp->data==data)) return;
	
	/*adjust next pointers*/
	for(i=0; i<=list.listLevel; i++){
		if(update[i]->next[i]!=temp)
			break;
		update[i]->next=temp->next[i];
	}
	free(temp);

	/*adjust header level*/
	while((list.listLevel>0) && (list.header->next[list.listLevel]==listHeader))
		list.listLevel;
}
	
struct ListNode *findElement(int data){
	int i;
	struct ListNode *temp=list.header;
	for(i=list.listLevel; i>=0; i--){
		while(temp->next[i]!=list.header && temp->next[i]->data<data)
			temp=temp->next[i];
	}
	temp=temp->next[0];
	if(temp!=list.header && temp->data==data) return temp;
	return 0;
}

/*command line: skipList maxnum skipList 2000: process 2000 sequential records*/
int main(int argc, char **argv{
	int i, *a, maxnum=atoi(argv[1]);
	initList();
	if((a=malloc(maxnum*sizeof(*a)))==0){
		fprintf(stderr, "insufficient memory (a)\n");
		exit(1);
	}
	for(i=0; i<maxnum; i++) a[i]=rand();
	printf("Random, %d items\n",maxnum);
	for(i=0; i<maxnum; i++)
		insertElement(a[i]);
	for(i=maxnum-1; i>=0; i--)
		findElement(a[i]);
	for(i=maxnum-1; i>=0; i--)
		deleteElement(a[i]);
	return 0;
}

10. Linked Lists: Problems & Solutions

Pro 1: Find nth node from the end of a Linked List

Solution 1: Brute-Force Method.

  • Start with the first node and count the number of nodes present after that node.
  • Time Complexity: O(n2), Space Complexity: O(1)

Solution 2: Hash Table

  • Create a hash table whose entries are <position of node ,node address>. By the time we traverse the complete list for creating the hash table, we can return the length-n+1th key value from hash table.
  • Time Complexity: O(n), Space Complexity: O(n)

Solution 3: Math

  • Find the length of list and compute M-n+1.
  • Time Complexity: O(n), Space Complexity: O(1)

Solution 4 (best): Two pointer

  • Use two pointers pNthNode and pTemp. Initially, both point to head node of the list. pNthNode starts moving only after pTemp has made n moves.
  • Time Complexity: O(n), Space Complexity: O(1)
struct ListNode *NthNodeFromEnd(struct ListNode *head, int NthNode){
	struct ListNode *slow=head, *fast=head;
	for(int count=0; count<NthNode; count++){
		if(fast)
			fast=fast->next;
	}
	while(fast){
		slow=slow->next;
		fast=fast->next;
	}
	if(slow)
		return slow;
	return NULL;
}		
/*
The judgement condition of the while loop is whether the node which 'fast' points to is exist.
That means, when 'fast' go to the last element, we will still execute the loop body until 'fast' point to NULL
while at that time the 'slow' points to the correct position.
Also notice that the index of list is start from zero, but the count of element is start from one.
eg: A[0] last 3rd / A[1] last 2nd / A[2] last 1st
*/	

Pro 2.1: Check Linked List is a Snail or Snake.

Solution 1: Brute-Force Approach

  • Start with the first node and see whether there is any node whose next pointer is the current node’s address because the repetition of next pointers indicates the existence of a loop.
  • Does it work? As per the algorithm, we are checking for the next pointer addresses,but how do we find the end of the linked list (otherwise we will end up in an infinite loop)?

Solution 2: Hash Table

  • Algorithm:
    1. Traverse the linked list nodes one by one.
    2. Check if the address of the node is available in the hash table or not.
    3. If it is already available in the table, that indicates that we are visiting the node that was already visited.
    4. If the address of node is not available in table, insert that node’s address into hash table.
    5. Continue this process until we reach the end of the linked list or we find the loop.
  • Time Complexity: O(n), Space Complexity: O(n)

Solution 3: Sort

  • Algorithm:
    1. Traverse the linked list nodes one by one and take all the next pointer values into an array.
    2. Sort the array that has the next node pointers.
    3. If there is a loop in the linked list, definitely two node pointers will be pointing to the same node.
    4. After sorting if there is a loop in the list, the nodes whose next pointers are the same will end up adjacent in the sorted array.
    5. If any such pair exists in the sorted array then we say the linked list has a loop in it.
  • Does it work? The above algorithm works only if we can find the length of the list. But if the list has a loop then we may end up in an infinite loop. Due to this reason the algorithm fails.

Solution 4: Floyd Cycle Finding Algorithm

  • It uses two pointers moving at different speeds to walk the linked list. Once they enter the loop they are expected to meet, which denotes that there is a loop.
  • Time Complexity: O(n), Space Complexity: O(1)
int DoesLinkedListHasLoop(struct ListNode *head){
	struct ListNode *slow=head, *fast=head;
	while(slow && fast &&fast->next){
		slow=slow->next;
		fast=fast->next->next;
		if(slow==fast)
			return 1;
	}
	return 0;
}

Pro 2.2: Find the Start Node of the Loop

Solution : Modification of Floyd Cycle Finding Method

  • After finding the loop in the lined list, we initialize the slow pointer to the head of the linked list. From that point onwards both slow and fast move only one node at a time.The point at which they meet is the start of the loop.
  • Time Complexity: O(n), Space Complexity: O(1)
int FindBeginofLoop(struct ListNode *head){
	struct ListNode *slow=head, *fast=head;
	int loopExists=0;
	while(slow && fast && fast->next){
		slow=slow->next;
		fast=fast->next->next;
		if(slow==fast){
			loopExists=1;
			break;
		}
	}
	if(loopExists){
		slow=head;
		while(slow!=fast){
			fast=fast->next;
			slow=slow->next;
		}
		return slow;
	}
	return NULL;
}	
  • Number Theory Proof

    1. Let’s confirm one fact: when two men move in the same direction on a circular track, one front of the other and one behind, the speed is not equal, so the fast one is sure to catch up with the slow one.
    2. Assume two men in the loop are in the distance of x x x at the beginning, the perimeter of loop is C C C, their velocities are 2 v 2v 2v and v v v respectively. Then the time they meet is t = x / ( 2 v − v ) = x / v t=x/(2v-v)=x/v t=x/(2vv)=x/v. Due to v t = x ≤ C vt=x\leq C vt=xC, we can draw a conclusion that from the start to their first encounter, the slow one will run no more than one lap.
      在这里插入图片描述
    3. Back to the figure, assume the outer ring part length is a a a, the slow pointer goes a distance of b b b when they meet together (we had proofed that slow one goes no more that one lap), c c c is the rest part of the loop.
    4. When they meet, the fast one had already finish n n n times of loop which means that the fast one goes a distance of a+n(b+c)+b. While the slow one goes a distance of a+b.
    5. Furthermore, at any moment, the fast pointer travels 2 times as far as the slow pointer. That gives us an equation: a+n(b+c)+b=2(a+b). Simplify it we have a=c+(n-1)(b+c)=c+(n-1)L.
    6. With this equivalent relationship, we will discover that: the distance from the encounter point to the entry plus the n − 1 n-1 n1 times of length of loop is exactly the distance from the head of the list to the entry point.
    7. Therefore,when the fast and slow pointers encounter, we apply an additional pointer start from the head of the list. Then it and slow pointer move one step each time until they meet at the entry.
  • Does it work if we use step 2 and 3 instead? Yes, but the complexity might be high.

Pro 2.3: Find the Length of the loop

Solution :

  • After finding the loop in the linked list, keep the slow ptr as it is. The fast ptr keeps on moving until it again comes back to slow ptr. While moving use a counter variable which increment at the rate of 1.
int FindLoopLength(struct ListNode *head){
	struct ListNode *slow=head, *fast=head;
	int loopExists=0, count=0;
	while(slow && fast &&fast->next){
		slow=slow->next;
		fast=fast->next->next;
		if(slow==fast){
			loopExists=1;
			break;
		}
	}
	if(loopExists){
		do{
			fast=fast->next;
			count++;
		}while(fast!=slow);
		return count;
	}
	return 0;
}	

Pro 3: Reverse a singly linked list

Solution 1: Iteration

  • Time Complexity: O(n), Space Complexity: O(1)
struct ListNode *ReverseList(struct ListNode *head){
	struct ListNode *prev=NULL, *curr=head;
	while(curr){
		ListNode* temp=curr->next;		//temp: original curr node's next
		curr->next=prev;
		prev=cur;
		curr=temp;
	}
	return prev;
}

Solution 2: Recursion

  • Time Complexity: O(n), Space Complexity: O(n)
  • The train of thought: What is the reverse of the empty list / one element list / n elements list?
struct ListNode* ReverseList(struct ListNode *head){
	if(head==NULL || head->next==NULL)
		return head;
	struct ListNode *secondNode=head->next;
	head->next=NULL; 										//unlink list or will get a cycle
	struct ListNode *reverseRest=ReverseList(secondNode);	//reverse everything from seond element on
	secondNode->next=head;									//join the two list						
	return reverseRest;	
}
struct ListNode* reverseList(struct ListNode* head) {		//Leetcode version
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode* newHead = reverseList(head->next);
    head->next->next = head;
    head->next = NULL;
    return newHead;
}

Pro 4. Find the Intersection Point

在这里插入图片描述
Solution 1: Brute-Force Approach

  • Time Complexity: O(mn), Space Complexity: O(1)

Solution 2: Hash Table

  • Time Complexity: O(n)+O(m) for creating the hash table plus canning the other list ,Space Complexity: O(n) or O(m)

Solution 3: Stacks

  • Algorithm:
    1. Create two stacks, one for the 1st list and one for the 2nd list.
    2. Traverse the lists and push all the node addresses onto the stacks respectively.
    3. Now compare the top node address of both stacks.
  • Time Complexity: O(n+m), Space Complexity: O(n+m)

Solution 4: Sort

  • Algorithm:
    1. Take two lists node pointers and keep them in two arrays and sort them respectively.
    2. Compare values at two indexes and increment the index according to whichever has the lower value.
  • Does it work? This algorithm failed due to store the node information but forget that there can be many repeated element.

Solution 5: Search

  • Algorithm:
    1. Create an array and keep all the next pointers of both the lists in the array.
    2. Find the 1st repeating element.
  • Time Complexity: O(n+m), Space Complexity: O(n+m)

Solution 6: Combine Sort and Search technique

  • Algorithm:
    1. Create an array and keep all the next pointers of the first list in the array.
    2. Sort these elements.
    3. For each of the seond list elements, search in the sorted array (assume using binary search O(logn)).
    4. Since we are scanning the second list one by one, the first repeating element that appears in the array is nothing but the merging point.
  • Time Complexity: O(Max(mlogm,nlogn)), Space Complexity: O(Max(n,m))

Solution 7: Efficient Approach

  • Find lengths of both lists and take the difference.
  • Time Complexity: O(max(m,n)), Space Complexity: O(1)
struct ListNode *FindIntersectingNode(struct ListNode *list1, struct ListNode *list2){
	int l1=0, l2=0, diff=0;
	struct ListNode *head1=list1, *head2=list2;
	while(head1){
		l1++;
		head1=head1->next;
	}
	while(head2){
		l2++;
		head2=head2->next;
	}
	if(l1<l2){
		head1=list2;
		head2=ist1;
		diff=l2-l1;
	}else{
		head1=list1;
		head2=list2;
		diff=l1-l2;					//head1 is the long list's head
	}
	for(int i=0; i<diff; i++)		//make two list equal length 		
		head1=head1->next;
	while(head1!=NULL && head2!=NULL){
		if(head1==head2)
			return head1->data;
		head1=head1->next;
		head2=head2->next;
	}
	return NULL;
}	

Pro 5: Find the middle of the Linked List

Note: If the list has an even number of the nodes, the middle node is defined as ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2
Solution 1: Brute-Force Approach
Solution 2: Find Length
Solution 3: Hash Table
Solution 4: Two Pointer

struct ListNode *FindMiddle(struct ListNode *head){
	struct ListNode *slow=head, fast=head;
	int i=0;						//fancy using a tag/flag
	while(fast->next){
		if(i==0){
			fast=fast->next;
			i=1;
		}else if(i==1){
			fast=fast->next;
			slow=slow->next;
			i=0;
		}
	}
	return slow;
}
/*
because the problem has give a def on even number which require $floor
cannot simply use the traditional approach fast->-> slow-> which result in the $ceil
swicth the if..else body can switch $floor to $ceil
*/

Pro 6: Display a Linked List from the end

Solution : Traverse recursively till the end. Or we could read them onto stacks.

void PrintListFromEnd(struct ListNode *head){
	if(!head)
		return;
	PrintListFromEnd(head->next);
	printf("%d",head->data);
}

Pro 7: Check whether the Length is Even or Odd

Solution : Traverse

int IsLinkedListLengthEven(struct ListNode *listHead){
	while(listHead && listHead->next)
		listHead=listHead->next->next;
	if(!listHead)
		return 1;		//even pointer NULL
	return 0;			//odd pointer last node
}	

Pro 8: Merge two sorted Lists

Solution 1: Recursive

struct ListNode *MergeSortedList(struct ListNode *a, struct ListNode *b){
	struct ListNode *res=NULL;
	if(a==NULL) return b;
	if(b==NULL) return a;
	if(a->data <= b->data){
		res=a;
		res->next=MergeSortedList(a->next, b);
	}else{
		res=b;
		res->next=MergeSortedList(b->next, a);
	}
	return res;
}

Solution 2: Iterative

struct ListNode *MergeSortedList(struct ListNode *a, struct ListNode *b){
	struct ListNode *prehead=(struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode *curr=prehead;
	while(a && b){
		if(a->data <= b->data){
			curr->next=a;
			a=a->next;
		}else{
			curr->next=b;
			b=b->next;
		}
		curr=curr->next;
	}
	curr->next = a==NULL ? b : a;
	struct ListNode *temp=prehead->next;
	free(prehead);
	return temp;
}

Pro 9.1: Reverse the linked list in Pairs

Solution 1: Recursive

struct ListNode *ReversePair(struct ListNode *head){
	struct ListNode *temp;
	if(head==NULL || head->next==NULL)
		return;
	else{
		/*reverse first pair*/
		temp=head->next;
		head->next=temp->next;
		temp->next=head;
		head= temp;
		/*call the method recursively for the rest of the list*/
		head->next->next=ReversePair(head->next->next);
		return head;
	}
}	

Solution 2: Iterative

struct ListNode *ReversePair(struct ListNode *head){
	struct ListNode *temp=NULL, *newHead=NULL, *curr=head;
	while(curr && curr->next){
		if(temp!=NULL)
			temp->next->next=curr->next;  //fancy method to find the next pair new head
		temp=curr->next;
		curr->next=curr->next->next;
		temp->next=curr;
		if(newHead==NULL)
			newHead=temp;		//fix the new head for return ,only works in the first loop		
		curr=curr->next;		//go next pair
	}
	return newHead;
}

Pro 9.2: Reverse blocks of K nodes in list

Solution :

  • Algorithm:
    1. Check if remaining list has K nodes: if yes get the pointer of K+1th node; else return.
    2. Reverse first K node.
    3. Set next of last node (after reversal) to K+1th node.
    4. Move to K+1th node.
    5. Go to step 1.
    6. K-1th node of first K nodes becomes the new head if available. Otherwise, we can return the head.
struct ListNode *GetKPlusOnethNode(int K, struct ListNode *head){
	struct ListNode *kth=head;
	if(!head)
		return head;
	for(int i=0; kth && i<K; i++, kth=kth->next);
	if(i==K && kth!=NULL)
		return Kth;
	return head->next;
}
int HasKnodes(int K, struct ListNode *head){
	for(int i=0; head && i<K; i++, head=head->next);
	if(i==K) return 1;
	return 0
}
struct ListNode *ReverseBlockOfKnodesInLinkedList(struct ListNode *head, int K){
	struct ListNode *curr=head, *temp, *next, newHead;
	if(K==0 || K==1)
		return head;
	if(HasKnodes(K-1, curr))
		newHead=GetKPlusOnethNode(K-1, curr);
	else
		newHead=head;
	while(curr && HasKnodes(K, curr)){
		temp=GetKPlusOnethNode(K, curr);
		int i=0;
		while(i<K){
			next=curr->next;
			curr->next=temp;
			temp=curr;
			curr=next;
			i++;
		}
	}
	return newHead;
}	

Pro 10: Split a Circular Linked List into two equal parts

Solution :

void SplitList(struct ListNode *head, struct ListNode **head1, struct ListNode **head2){
	struct ListNode *slow=head, *fast=head;
	if(head==NULL)
		return;
	while(fast->next!=head && fast->next->next!=head){
		fast=fast->next->next;
		slow=slow->next;			//traverse
	}
	if(fast->next->next==head)		//even element
		fast=fast->next;
	*head1=head;					//set first half head
	if(head->next!=head)			//set second half head
		*head2=slow->next;
	fast->next=slow->next;			//make second half circular
	slow->next=head;				//make first half circular
}	

Pro 11: Check if the linked list is Palindrome or not

Solution : pro 9 + pro10

  • Algorithm :
    1. Get the middle of the linked list.
    2. Reverse the first half of the linked list.
    3. Compare the first half and the second half.
    4. Construct the original linked list by reversing the second half again and attaching it back to the first half.

Pro 12: Josephus Circle

N people have decided to elect a leader by arranging themselves in a circle and eliminating every Mth person around the circle, closing ranks as each person drops out. Find which person will be the last one remaining (with rank 1). Assume the input is a circular linked list with N nodes and each node has a number (range 1 to N) associated with it. The head node has number 1 as data.

Solution :

struct ListNode *GetJosephusPosition(){
	struct ListNode *p, *q;
	int n, m;                      
	printf("Enter N(number of players):"); scanf("%d", &n);
	printf("Enter M(every M-th player gets eliminated):");scanf("%d", &m);
	/*create circular linked list containing all the playyers*/
	p=q=malloc(sizeof(struct node));
	p->data=1;
	for(int i=2; i<=n; i++){
		p->next=malloc(sizeof(struct node));
		p=p->next;
		p->data=i;
	}
	p->next=q;
	/*Eliminate every M-th player as long as more than one player remains*/
	for(int count=n; count>1; --count){
		for(int i=0; i<m-1; i++)
			p=p->next;
		p->next=p->next->next;   //remove;
	}
	printf("last player left standing (Josephus Position) is %d\n", p->data);
}

Pro 13: Clone a linked list with Next Pointer and Random pointer

Solution 1: Hash Table

  • Algorithm :
    1. Scan the original list and for each node X, create a new node Y with data of X, then store the pair (X,Y) in hash table using X as a key. Note that during this scan set Y next and Y random to NULL and we will fix them in the next scan.
    2. Now for each node X in the original list we have a copy Y storeed in our hash table. We scan th original list again and set the pointers building the new list.
typedef struct ListNode{
	int data;
	struct ListNode *next;
	struct ListNode *random;
};
struct ListNode *Clone(struct ListNode *head){
	struct ListNode *x, *y;
	struct HashTable *HT=CreateHashTable();
	x=head;
	while(x){
		y=(struct ListNode*)malloc(sizeof(struct ListNode *));
		y->data=x->data;
		y->next=NULL;
		Y->random=NULL;
		HT.insert(x, y);
		x=x->next;
	}
	x=head;
	while(x){
		y=HT.get(x);
		y->next=HT.get(x->next);
		y.setRandom=HT.get(x->random);
		x=x->next;
	}
	return HT.get(head);
}

Solution 2: without extra space

void Clone(struct ListNode *head){
	struct ListNode *curr, *temp;

	/*step1: put curr->random in temp->next*/
	/*so that we can reuse the temp->random field to point to temp*/
	curr=head;
	while(curr){
		temp=(struct ListNode*)malloc(sizeof(struct ListNode*));
		temp->data=curr->data;
		temp->next=curr->random;
		curr->random=temp;
		curr=curr->next;
	}
	
	/*step2: set temp->random*/
	/*temp->next is the old copy of the node which temp->random should point to, so curr->next->random is the new copy*/
	curr=head;
	while(curr){
		temp=curr->random;
		temp->random=curr->next->random;
		curr=curr->next;
	}

	/*step3: repair damage to old list and fill in next pointer in new list*/
	curr=head;
	while(curr){
		temp=curr->random;
		curr->random=remp->next;
		temp->next=temp->next->random;
		curr=curr->next;
	}
}

Pro 14: Delete node

We are given a pointer to a node (not the tail node) in a singly linked list.

Solution : Modify data

  • To delete a node, we have to adjust the next pointer of the previous node to point to next node instead of the current one. But we cannot access to the previous node so we cannot redirect its pointer.
void deleteNode(struct ListNode *node){
	struct ListNode *temp=node->next;
	node->data=node->next->data;
	node->next=temp->next;
	free(temp);
}
  • Time Complexity: O(1), Space Complexity: O(1).

Pro 15.1: Find Modular Node from Begin

Solution :

struct ListNode *modularNodeFromBegin(struct ListNode *head, int k){
	struct ListNode *ans;
	if(k>=0) return NULL;
	int i=0;
	while(head){
		if(i%k==0)
			ans=head;
		i++;
		head=head->next;
	}
	return ans;
}	

Pro 15.2: Find Modular Node from End

Given a singly linked list, write a function to find the first from the end whose n%k==0, where n is the number of elements in the list which is not known in advance and k is an integer constant.

Solution :

struct ListNode *modularNodeFromEnd(struct ListNode *head, int k){
	struct ListNode *ans=head;
	if(k<=0) return NULL;
	for(int i=0; i<k; i++){
		if(head)
			head=head->next;
		else 
			return NULL;
	}
	while(head){				//two pointers with space
		head=head->next;		//when head move the NULL
		ans=ans->next			//ans is on the position
	}
	return ans;
}

Pro 15.3: Find Fractional Node

Given a singly linked list, write a function to find the n k \frac{n}{k} knth element.

Solution :

struct ListNode *fractionNode(struct ListNode *head, int k){
	struct ListNode *ans=NULL;
	if(k<=0) return NULL;
	for(int i=0; head!=NULL; i++){
		if(i%k==0){
			if(ans==NULL)
				ans=head;		//index begin from 0
			else
				ans=ans->next;
		}
		head=head->next;
	}
	return ans;
}		

Pro 15.4: Find n \sqrt{n} n Node

Given a singly linked list, write a function to find the n \sqrt{n} n th element.
Solution :

struct ListNode *sqrtNode(struct ListNode *head){
	struct ListNode *ans=NULL;
	int i=1, j=1;
	for(; head; head=head->next){
		if(i=j*j){
			if(ans==NULL)
				ans=head;
			else
				ans=ans->next;
			j++;
		}
		i++;
	}	
	return ans;
}

Pro 16.1: Merge two Lists Alternately

{ A 1 A_1 A1 , B 1 B_1 B1, A 2 A_2 A2 , B 2 B_2 B2 A m A_m Am , B m B_m Bm, A m + 1 A_{m+1} Am+1, A m + 2 A_{m+2} Am+2 A n A_n An},if n ≥ m n\geq m nm.
{ A 1 A_1 A1 , B 1 B_1 B1, A 2 A_2 A2 , B 2 B_2 B2 A n A_n An , B n B_n Bn, B n + 1 B_{n+1} Bn+1, B n + 2 B_{n+2} Bn+2 B m B_m Bm},if m ≥ n m\geq n mn.

Solution :

struct ListNode *AlternateMerge(struct ListNode *list1, struct ListNode *list2){
	/*dummy-headed linked list*/
	/*idea: put an empty node before first node*/
	/*benefit: Since every non-empty node has a predecessor, it is a strategy for reducing the number of special cases required for list processing*/
	struct ListNode *dummy=(struct ListNode*)malloc(sizeof(struct ListNode)));		
	dummy->next=NULL;
	struct ListNode *head=dummy;
	while(list1 && list2){
		head->next=list1;
		head=head->next;
		list1=list1->next;
		head->next=list2;
		head=head->next;
		list2=list2->next;
	}
	head->next = list1 ? list2 : list1;
	head=dummy->next;
	free(dummy);
	return head;
}

Pro16.2: Reorder List

{ A 1 A_1 A1 , A 2 A_2 A2, A 3 A_3 A3 A n − 1 A_{n-1} An1, A n A_n An} into
{ A 1 A_1 A1 , A n A_n An, A 2 A_2 A2 , A n − 1 A_{n-1} An1…}

Solution :

  • Algorithm:
    1. Use Two Pointer approach to find the middle of the linked list.
    2. Reverse the right half then do a in place merge of two halves.

Pro 17: Modify linked list such that Even Number Appear before all Odd number

Solution :

struct ListNode *exchangeEvenOddList(struct ListNode *head){
	struct ListNode *oddlist=NULL, *evenlist=NULL;	//initialize the head
	struct ListNode *oddend=NULL, *evenend=NULL;    //create tail variable
	struct ListNode *itr=head;

	if(head==NULL) return;
	else{
		while(itr){
			if(itr->data%2==0){
				if(evenlist==NULL){			//first even node
					evenlist=evenend=itr;
				}
				else{
					evenend->next=itr;
					evenend=itr;
				}
			}
			else{
				if(oddlist==NULL){
					oddlist=oddend=itr;
				}
				else{
					oddend->next=itr;
					oddend=itr;
				}
			}
			itr=itr->next;
		}
		evenend->next=oddlist;
		return head;
	}
}	

Pro 18: Add Digit

Given two linked lists, each node with one integer digit, add two numbers represented by the two linked list. Note that the head node contains the most significant digit of the number.

Solution :

  • Since the integer addition starts from the least significant digit, we first need to visit the last node of both lists and add them up, create a new node to store the result, take care of carry if any, and link the resulting node which will be added to the second least significant node and continue.
void addListNumberWrapper(struct ListNode *list1, struct ListNode *list2, int *carry, struct ListNode **result){
	int length1=0, length2=0, diff=0;
	struct ListNode *curr=list1;
	while(curr){
		cur=cur->next;
		length1++;
	}
	curr=list2;
	while(curr){
		cur=cur->next;
		length2++;
	}
	if(length1<length2){
		curr=list1;
		list1=list2;
		list2=curr;
	}
	diff=abs(length1-length2);
	curr=list1;
	while(diff--)
		curr=curr->next;
	addListNum(curr, list2, carry, result);
	diff=abs(length1-length2);
	addRemainNum(list1, carry, result, diff);
	if(*carry){													//the most significant digit results in a carry
		struct ListNode *temp=(struct ListNode *)malloc(sizeof(struct ListNode));	//need to create an extra node
		temp->next=(*result);
		*result=temp;
	}
	return;
}
void addListNum(struct ListNode *list1, struct ListNode *list2, int *carry, struct ListNode **result){
	int sum;
	if(!list1) return ;
	addListNum(list1->next, list2->next, carry, result);
	struct ListNode *temp=(struct ListNode *)malloc(sizeof(struct ListNode));
	sum=list1->data+list2->data+(*carry);
	*carry=sum/10;
	sum=sum%10;
	temp->data=sum;
	temp->next=(*result);
	*result=temp;
	return ;
}
void addRemainNum(struct ListNode list1, int *carry, struct ListNode **result, int diff){
	int sum=0;
	if(!list || diff==0) return;
	addRemainNum(list1->next, carry, result, diff-1);
	struct ListNode *temp=(struct ListNode*)malloc(sizeof(struct ListNode));
	sum=list1->data+(*carry);
	*carry=sum/10;
	temp->data=sum;
	temp->next=(*result);
	*result=temp;
	return ;
}
  • Time Complexity: O(max(m,n)), Space Complexity: O(min(m,n))
/*leetcode version*/
/*need reverse the list and reverse back (omitted)*/
/*make the head node store the least significant bit*/
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    struct ListNode *head=NULL,*tail=NULL;
    int carry=0;
    while(l1||l2){
        int n1= l1?l1->val:0;
        int n2= l2?l2->val:0;
        int sum=n1+n2+carry;
        if(!head){
            head=tail=malloc(sizeof(struct ListNode));
            tail->val=sum%10;
            tail->next=NULL;
        }else{
            tail->next=malloc(sizeof(struct ListNode));
            tail->next->val=sum%10;
            tail=tail->next;
            tail->next=NULL;
        }
        carry=sum/10;
        if(l1) l1=l1->next;
        if(l2) l2=l2->next;
    }
    if(carry>0){
        tail->next=malloc(sizeof(struct ListNode));
        tail->next->val=carry;
        tail->next->next=NULL;
    }
    return head;
}
  • Time Complexity: O(max(m,n)), Space Complexity: O(1)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值