πŸ“–
Wiki
CNCFSkywardAIHuggingFaceLinkedInKaggleMedium
  • Home
    • πŸš€About
  • πŸ‘©β€πŸ’»πŸ‘©Freesoftware
    • πŸ‰The GNU Hurd
      • πŸ˜„The files extension
      • πŸ“½οΈTutorial for starting
      • 🚚Continue Working for the Hurd
      • πŸš΄β€β™‚οΈcgo
        • πŸ‘―β€β™€οΈStatically VS Dynamically binding
        • 🧌Different ways in binding
        • πŸ‘¨β€πŸ’»Segfault
      • πŸ›ƒRust FFI
    • πŸ§šπŸ»β€β™‚οΈProgramming
      • πŸ“–Introduction to programming
      • πŸ“–Mutable Value Semantics
      • πŸ“–Linked List
      • πŸ“–Rust
        • πŸ“–Keyword dyn
        • πŸ“–Tonic framework
        • πŸ“–Tokio
        • πŸ“–Rust read files
  • πŸ›€οΈAI techniques
    • πŸ—„οΈframework
      • 🧷pytorch
      • πŸ““Time components
      • πŸ““burn
    • 🍑Adaptation
      • 🎁LoRA
        • ℹ️Matrix Factorization
        • πŸ“€SVD
          • ✝️Distillation of SVD
          • 🦎Eigenvalues of a covariance matrix
            • 🧧Eigenvalues
            • πŸͺCovariance Matrix
        • πŸ›«Checkpoint
      • 🎨PEFT
    • πŸ™‹β€β™‚οΈTraining
      • πŸ›»Training with QLoRA
      • 🦌Deep Speed
    • 🧠Stable Diffusion
      • πŸ€‘Stable Diffusion model
      • πŸ“ΌStable Diffusion v1 vs v2
      • πŸ€Όβ€β™€οΈThe important parameters for stunning AI image
      • ⚾Diffusion in image
      • 🚬Classifier Free Guidance
      • ⚜️Denoising strength
      • πŸ‘·Stable Diffusion workflow
      • πŸ“™LoRA(Stable Diffusion)
      • πŸ—ΊοΈDepth maps
      • πŸ“‹CLIP
      • βš•οΈEmbeddings
      • πŸ• VAE
      • πŸ’₯Conditioning
      • 🍁Diffusion sampling/samplers
      • πŸ₯ Prompt
      • πŸ˜„ControlNet
        • πŸͺ‘Settings Explained
        • 🐳ControlNet with models
    • πŸ¦™Large Language Model
      • ☺️SMID
      • πŸ‘¨β€πŸŒΎARM NEON
      • 🍊Metal
      • 🏁BLAS
      • πŸ‰ggml
      • πŸ’»llama.cpp
      • 🎞️Measuring model quality
      • πŸ₯žType for NNC
      • πŸ₯žToken
      • πŸ€Όβ€β™‚οΈDoc Retrieval && QA with LLMs
      • Hallucination(AI)
    • 🐹diffusers
      • πŸ’ͺDeconstruct the Stable Diffusion pipeline
  • 🎹Implementing
    • πŸ‘¨β€πŸ’»diffusers
      • πŸ“–The Annotated Diffusion Model
  • 🧩Trending
    • πŸ“–Trending
      • πŸ“–Vector database
      • 🍎Programming Languages
        • πŸ“–Go & Rust manage their memories
        • πŸ“–Performance of Rust and Python
        • πŸ“–Rust ownership and borrowing
      • πŸ“–Neural Network
        • 🎹Sliding window/convolutional filter
      • Quantum Machine Learning
  • 🎾Courses Collection
    • πŸ“–Courses Collection
      • πŸ“šAcademic In IT
        • πŸ“Reflective Writing
      • πŸ“–UCB
        • πŸ“–CS 61A
          • πŸ“–Computer Science
          • πŸ“–Scheme
          • πŸ“–Python
          • πŸ“–Data Abstraction
          • πŸ“–Object-Oriented Programming
          • πŸ“–Interpreters
          • πŸ“–Streams
      • 🍎MIT Algorithm Courses
        • 0️MIT 18.01
          • 0️Limits and continuity
          • 1️Derivatives
          • 3️Integrals
        • 1️MIT 6.042J
          • πŸ”’Number Theory
          • πŸ“ŠGraph Theory
            • 🌴Graph and Trees
            • 🌲Shortest Paths and Minimum Spanning Trees
        • 2️MIT 6.006
          • Intro and asymptotic notation
          • Sorting and Trees
            • Sorting
            • Trees
          • Hashing
          • Graphs
          • Shortest Paths
          • Dynamic Programming
          • Advanced
        • 3️MIT 6.046J
          • Divide and conquer
          • Dynamic programming
          • Greedy algorithms
          • Graph algorithms
Powered by GitBook
On this page
  • Linked List
  • Singly Linked List
  • Design a Linked List
  • Doubly Linked List
  • Why we need Doubly Linked List? Is it more efficient than Singly Linked List?
  • What are the techniques to solve the linked list problems?
  • Questions

Was this helpful?

Edit on GitHub
  1. πŸ‘©Freesoftware
  2. Programming

Linked List

Linked List

It is a abstract data type that represents a sequence of nodes. In other words, it can be implemented in many ways. So, here is my suggestion, try to implement it according to the situation rather than only one way.

Singly Linked List

This structure is the most common one. It is a linear data structure where each element is a separate object. Each element is called a node and every node has two parts, data and pointer to the next node. The first node is called head and the last node is called tail. The tail node points to null.

Design a Linked List

Design a Linked List with head and size attributes

example=[1,2,3,4,5,6]
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
class Node:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

class LinkedList:
    def __init__(self):
        self.head = None
        self.size = 0
    
    def get(self, index: int) -> int:
        if index<0 or index>=self.size:
            return -1
        curr = self.head
        for _ in range(index):
            curr=curr.next
        if curr is None:
            return -1
        return curr.val
    
    def addAtHead(self, val: int) -> None:
        self.head=Node(val, self.head)
        self.size += 1
    
    def addAtTail(self, val: int) -> None:
        if self.head is None:
            self.head = Node(val)
        else:
            curr=self.head
            while curr.next:
                curr=curr.next
            curr.next=Node(val)
        self.size += 1
    
    def addAtIndex(self, index: int, val: int) -> None:
        if index<0 or index>self.size:
            return
        if index==0:
            self.addAtHead(val)
        else:
            curr=self.head
            for _ in range(index-1):
                curr=curr.next
            curr.next=Node(val, curr.next)
            self.size += 1
    
    def deleteAtIndex(self, index: int) -> None:
        if index<0 or index>=self.size:
            return
        if index==0:
            self.head=self.head.next
        else:
            curr=self.head
            for _ in range(index-1):
                curr=curr.next
            curr.next=curr.next.next
        self.size -= 1

The Singly Linked List in Rust

Box: A smart pointer type for heap allocation. Option is a type that represents an optional value: every Option is either Some and contains a value, or None, and does not.

struct Node {
    val: i32,
    next: Option<Box<Node>>,
}

impl Node {
    fn new(val: i32) -> Self {
        Node {val, next: None}
    }
}

struct MyLinkedList {
    head: Option<Box<Node>>,
    size: i32,
}

impl MyLinkedList {
    fn new() -> Self{
        MyLinkedList {head: None, size: 0}
    }

    fn get(&self, index: i32) -> i32 {
        if index<0 || index>=self.size{
            return -1;
        }
        let mut curr = &self.head;
        let mut i =0;
        while let Some(node) = curr{
            if i ==index{
                return node.val;
            }
            i+=1;
            curr = &node.next;
        }
        -1
    }

    fn add_at_head(&mut self, val: i32){
        let mut node =Box::new(Node::new(val));
        node.next = self.head.take();
        self.head=Some(node);
        self.size+=1;
    }

    fn add_at_tail(&mut self, val: i32){
        let node=Box::new(Node::new(val));
        let mut curr = &mut self.head;
        while let Some(node)=curr{
            curr=&mut node.next;
        }
        *curr=Some(node);
        self.size+=1;
    }

    fn add_at_index(&mut self, inex: i32, val:i32){
        if index<0 || index>self.size{
            return;
        }
        if index==0{
            self.ad_at_head(val);
            return;
        }
        let mut curr = &mut self.head;
        let mut i = 0;
        while let Some(node) =curr{
            if i==index-1{
                let mut new_node = Box::new(Node::new(val));
                new_node.next=node.next.take();
                node.next=Some(new_node);
                self.size+=1;
            }
            i+=1;
            curr=&mut node.next
        }
    }

    fn delete_at_index(&mut self, index: i32){
        if index<0 || index>=self.size{
            return;
        }
        if index==0{
            self.head=self.head.take().unwrap().next.take();
            self.size-=1;
            return;
        }
        let mut curr = &mut self.head;
        let mut i = 0;
        while let Some(node)=curr{
            if i==index-1{
                node.next=node.next.take().unwrap().next;
                self.size-=1;
                return;
            }
            i+=1;
            curr=&mut node.next;
        }
    }
}

Design a Linked List with head, tail and size attributes


class Node:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0
    
    def get(self, index: int) -> int:
        if index<0 or index>=self.size:
            return -1
        curr = self.head
        for _ in range(index):
            curr=curr.next
        if curr is None:
            return -1
        return curr.val
    
    def addAtHead(self, val: int) -> None:
        self.head = Node(val, self.head)
        if self.tail is None:
            self.tail = self.head
        self.size += 1
        
    def addAtTail(self, val: int) -> None:
        if self.tail is None:
            self.head = self.tail = Node(val)
        else:
            self.tail.next = Node(val)
            self.tail = self.tail.next
        self.size += 1
    
    def addAtIndex(self, index: int, val: int) -> None:
        if index<0 or index>self.size:
            return
        if index==0:
            self.addAtHead(val)
        elif index==self.size:
            self.addAtTail(val)
        else:
            curr = self.head
            for _ in range(index-1):
                curr = curr.next
            curr.next = Node(val, curr.next)
            self.size += 1
    
    def deleteAtIndex(self, index: int) -> None:
        if index<0 or index>=self.size:
            return
        curr = self.head
        if index==0:
            self.head = self.head.next
            if self.head is None:
                self.tail = None
        else:
            for _ in range(index-1):
                curr = curr.next
            curr.next = curr.next.next
            if index==self.size-1:
                self.tail = curr
        self.size -= 1

What is the difference between the two implementations?

The first implementation is more efficient than the second one. The second implementation is more readable than the first one.

The first implementation is more efficient because it does not need to check if the tail is None or not. The second implementation is more readable because it does not need to check if the index is 0 or not.

Doubly Linked List

This structure is similar to the singly linked list. The only difference is that each node has two pointers, one points to the next node and the other points to the previous node. The first node is called head and the last node is called tail. The head node points to null and the tail node points to null.

class Node:
    def __init__(self, val, next=None, prev=None):
        self.val = val
        self.next = next
        self.prev = prev

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0
    
    def get(self, index: int) -> int:
        if index<0 or index>=self.size:
            return -1
        curr = self.head
        for _ in range(index):
            curr=curr.next
        if curr is None:
            return -1
        return curr.val
    
    def addAtHead(self, val: int) -> None:
        self.head = Node(val, self.head)
        if self.tail is None:
            self.tail = self.head
        else:
            self.head.next.prev = self.head
        self.size += 1
    
    def addAtTail(self, val: int) -> None:
        if self.tail is None:
            self.head = self.tail = Node(val)
        else:
            self.tail.next = Node(val, None, self.tail)
            self.tail = self.tail.next
        self.size += 1
    
    def addAtIndex(self, index: int, val: int) -> None:
        if index<0 or index>self.size:
            return
        if index==0:
            self.addAtHead(val)
        elif index==self.size:
            self.addAtTail(val)
        else:
            curr = self.head
            for _ in range(index-1):
                curr = curr.next
            curr.next = Node(val, curr.next, curr)
            curr.next.next.prev = curr.next
            self.size += 1
    
    def deleteAtIndex(self, index: int) -> None:
        if index<0 or index>=self.size:
            return
        curr = self.head
        if index==0:
            self.head = self.head.next
            if self.head is None:
                self.tail = None
            else:
                self.head.prev = None
        else:
            for _ in range(index-1):
                curr = curr.next
            curr.next = curr.next.next
            if index==self.size-1:
                self.tail = curr
            else:
                curr.next.prev = curr
        self.size -= 1

Why we need Doubly Linked List? Is it more efficient than Singly Linked List?

Doubly Linked List is useful when we need to traverse the linked list in both forward and backward directions. It provides a prev pointer in addition to the next pointer, which allows us to traverse the list in reverse order. However, it requires more memory than a Singly Linked List due to the extra prev pointer. In terms of time complexity, both types of linked lists have the same time complexity for most operations, such as insertion and deletion.

What are the techniques to solve the linked list problems?

For exmaple, how to find the linked list middle node, or it has a cycle or not.

Here is an example of checking if the linked list has a cycle or not.

# Definition for singly-linked list.
class Node:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]):
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False

Questions

PreviousMutable Value SemanticsNextRust

Last updated 1 year ago

Was this helpful?

πŸ‘©β€πŸ’»
πŸ§šπŸ»β€β™‚οΈ
πŸ“–
Two Pointers
Design Linked List
Two Pointer in Linked List