本文来源:
Linked List | Set 1 (Introduction)
Linked List | Set 2 (Inserting a node)
Linked List | Set 3 (Deleting a node)
Find Length of a Linked List (Iterative and Recursive) (todo:在结构中保持节点数信息)
Search an element in a Linked List (Iterative and Recursive)
Pairwise swap adjacent nodes of a linked list by changing pointers | Set 2 todo:交换相邻两个节点
Program to reverse a linked list using Stack ,链表倒序重新组织
Binary Search on Singly Linked List
Find the middle of a given linked list in C and Java
、正序、逆序插入
基础知识
介绍:链表与数组一样,同属于线性表的一个子集。不同之处在于链表元素并不需要存储到一块连续的内存空间;
链表中的元素通过指针来链接并维护各个节点之间的联系,可使用连续的内存空间、亦可不使用连续的内存空间。
使用链表的原因:
1、数组类型长度是固定的,一旦申明不可以修改长度。在实际使用中我们必须事先知道元素数量的上限。
2、实际使用过程中分配的数组上限的内存空间,而事实不一定占用那么多空间。
3、插入删除成本高,必须要进行位移。插入平均位移n/2个元素,删除移动(n-1)/2个元素。最理想的时间成本
O(1)即被删除/插入元素后面没有元素。
链表有点:
1、动态分配内存,即用即申请,占用空间大小也是动态的。
2、插入、删除方便,特别是在头和尾,修改指针地址即可。
缺点:
1、不可以随机访问,必须遍历。二分查找时间复杂度为O(n),数组二分查找时间复杂度O(log2n) (注:O(n)> O(log2n))
2、列表中每个节点还需要额外的内存空间存储指针域(链)数据
3、数组中元素占用的存储空间是连续的,可以根据索引进行访问元素,链表不支持此操作。
数据展现:
链表第一个节点叫头节点。如果链表为空,数据域、指针域(链、引用)都是NULL
c语言中通常用结构展示数据,在java或者C#中用类和另外一个节点类(Node)来表示,LinkedList类包含Node类型的引用。
创建3个节点并遍历
c语言:
// A simple C program to introduce // a linked list #include <stdio.h> #include <stdlib.h> // A linked list node struct Node { int data; struct Node* next; }; // This function prints contents of linked list starting from // the given node void printList(struct Node* n) { while (n != NULL) { printf(" %d ", n->data); n = n->next; } } // Program to create a simple linked // list with 3 nodes int main() { struct Node* head = NULL; struct Node* second = NULL; struct Node* third = NULL; // allocate 3 nodes in the heap head = (struct Node*)malloc(sizeof(struct Node)); second = (struct Node*)malloc(sizeof(struct Node)); third = (struct Node*)malloc(sizeof(struct Node)); /* Three blocks have been allocated dynamically. We have pointers to these three blocks as first, second and third head second third | | | | | | +---+-----+ +----+----+ +----+----+ | # | # | | # | # | | # | # | +---+-----+ +----+----+ +----+----+ # represents any random value. Data is random because we haven’t assigned anything yet */ head->data = 1; // assign data in first node head->next = second; // Link first node with // the second node /* data has been assigned to the data part of the first block (block pointed by the head). And next pointer of first block points to second. So they both are linked. head second third | | | | | | +---+---+ +----+----+ +-----+----+ | 1 | o----->| # | # | | # | # | +---+---+ +----+----+ +-----+----+ */ // assign data to second node second->data = 2; // Link second node with the third node second->next = third; /* data has been assigned to the data part of the second block (block pointed by second). And next pointer of the second block points to the third block. So all three blocks are linked. head second third | | | | | | +---+---+ +---+---+ +----+----+ | 1 | o----->| 2 | o-----> | # | # | +---+---+ +---+---+ +----+----+ */ third->data = 3; // assign data to third node third->next = NULL; /* data has been assigned to data part of third block (block pointed by third). And next pointer of the third block is made NULL to indicate that the linked list is terminated here. We have the linked list ready. head | | +---+---+ +---+---+ +----+------+ | 1 | o----->| 2 | o-----> | 3 | NULL | +---+---+ +---+---+ +----+------+ Note that only head is sufficient to represent the whole list. We can traverse the complete list by following next pointers. */ printList(head); return 0; }
输出结果:
1 2 3
java:
// A simple Java program to introduce a linked list public class LinkedList { Node head; // head of list /* Linked list Node. This inner class is made static so that main() can access it */ static class Node { int data; Node next; Node(int d) { data = d; next = null; } // Constructor } /* This function prints contents of linked list starting from head */ public void printList() { Node n = head; while (n != null) { System.out.print(n.data + " "); n = n.next; } } /* method to create a simple linked list with 3 nodes*/ public static void main(String[] args) { /* Start with the empty list. */ LinkedList llist = new LinkedList(); llist.head = new Node(1); Node second = new Node(2); Node third = new Node(3); /* Three nodes have been allocated dynamically. We have references to these three blocks as first, second and third llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | null | | 2 | null | | 3 | null | +----+------+ +----+------+ +----+------+ */ llist.head.next = second; // Link first node with the second node /* Now next of the first Node refers to the second. So they both are linked. llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | o-------->| 2 | null | | 3 | null | +----+------+ +----+------+ +----+------+ */ second.next = third; // Link second node with the third node /* Now next of the second Node refers to third. So all three nodes are linked. llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | o-------->| 2 | o-------->| 3 | null | +----+------+ +----+------+ +----+------+ */ llist.printList(); } }
c#
using System; public class LinkedList { Node head; // head of list /* Linked list Node. This inner class is made static so that main() can access it */ public class Node { public int data; public Node next; public Node(int d) { data = d; next = null; } // Constructor } /* This function prints contents of linked list starting from head */ public void printList() { Node n = head; while (n != null) { Console.Write(n.data + " "); n = n.next; } } /* method to create a simple linked list with 3 nodes*/ public static void Main(String[] args) { /* Start with the empty list. */ LinkedList llist = new LinkedList(); llist.head = new Node(1); Node second = new Node(2); Node third = new Node(3); /* Three nodes have been allocated dynamically. We have references to these three blocks as first, second and third llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | null | | 2 | null | | 3 | null | +----+------+ +----+------+ +----+------+ */ llist.head.next = second; // Link first node with the second node /* Now next of first Node refers to second. So they both are linked. llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | o-------->| 2 | null | | 3 | null | +----+------+ +----+------+ +----+------+ */ second.next = third; // Link second node with the third node /* Now next of the second Node refers to third. So all three nodes are linked. llist.head second third | | | | | | +----+------+ +----+------+ +----+------+ | 1 | o-------->| 2 | o--------> | 3 | null | +----+------+ +----+------+ +----+------+ */ llist.printList(); } }
插入节点
在链表中插入节点有以下三种情形:
1、在链表头部
2、在特定节点
3、在链表尾部
1)在头部添加(4步操作)
新节点添加到头部,将成为新的头节点,以下将节点添加到链表头部的函数是push,push接收指向链表头部的指针,然后将指针修改
指向新的节点:
c语言:
/* Given a reference (pointer to pointer) to the head of a list and an int, inserts a new node on the front of the list. */ void push(struct Node** head_ref, int new_data) { /* 1. allocate node */ struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); /* 2. put in the data */ new_node->data = new_data; /* 3. Make next of new node as head */ new_node->next = (*head_ref); /* 4. move the head to point to the new node */ (*head_ref) = new_node; }
Java:
/* This function is in LinkedList class. Inserts a new Node at front of the list. This method is defined inside LinkedList class shown above */ public void push(int new_data) { /* 1 & 2: Allocate the Node & Put in the data*/ Node new_node = new Node(new_data); /* 3. Make next of new Node as head */ new_node.next = head; /* 4. Move the head to point to new Node */ head = new_node; }
push时间复杂度O(1)
2)在特定节点(5步操作)
c语言:
/* Given a node prev_node, insert a new node after the given prev_node */ void insertAfter(struct Node* prev_node, int new_data) { /*1. check if the given prev_node is NULL */ if (prev_node == NULL) { printf("the given previous node cannot be NULL"); return; } /* 2. allocate new node */ struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); /* 3. put in the data */ new_node->data = new_data; /* 4. Make next of new node as next of prev_node */ new_node->next = prev_node->next; /* 5. move the next of prev_node as new_node */ prev_node->next = new_node; }
java:
/* This function is in LinkedList class. Inserts a new node after the given prev_node. This method is defined inside LinkedList class shown above */ public void insertAfter(Node prev_node, int new_data) { /* 1. Check if the given Node is null */ if (prev_node == null) { System.out.println("The given previous node cannot be null"); return; } /* 2. Allocate the Node & 3. Put in the data*/ Node new_node = new Node(new_data); /* 4. Make next of new Node as next of prev_node */ new_node.next = prev_node.next; /* 5. make next of prev_node as new_node */ prev_node.next = new_node; }
因为已经给定了节点,所以insertAfter时间复杂为O(1)
3)在链表尾部(6个步骤)
我们需要遍历链表,得到最后一个节点,然后把新节点追加到最后,然后让他成为新的尾部节点
c语言:
/* Given a reference (pointer to pointer) to the head of a list and an int, appends a new node at the end */ void append(struct Node** head_ref, int new_data) { /* 1. allocate node */ struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); struct Node *last = *head_ref; /* used in step 5*/ /* 2. put in the data */ new_node->data = new_data; /* 3. This new node is going to be the last node, so make next of it as NULL*/ new_node->next = NULL; /* 4. If the Linked List is empty, then make the new node as head */ if (*head_ref == NULL) { *head_ref = new_node; return; } /* 5. Else traverse till the last node */ while (last->next != NULL) last = last->next; /* 6. Change the next of last node */ last->next = new_node; return; }
java:
/* Appends a new node at the end. This method is defined inside LinkedList class shown above */ public void append(int new_data) { /* 1. Allocate the Node & 2. Put in the data 3. Set next as null */ Node new_node = new Node(new_data); /* 4. If the Linked List is empty, then make the new node as head */ if (head == null) { head = new Node(new_data); return; } /* 4. This new node is going to be the last node, so make next of it as null */ new_node.next = null; /* 5. Else traverse till the last node */ Node last = head; while (last.next != null) last = last.next; /* 6. Change the next of last node */ last.next = new_node; return; }
因为需要从头到尾循环,所以append时间复杂度为O(n),其中n是节点的个数。
当然你可以修改结构,保存尾节点,使其复杂度降为O(1)
删除节点(3步操作)
删除步骤
1) 找到待删除节点的前驱
2) 修改前驱节点的指针域指向待删除节点的后继节点
3)释放待删除的节点的内存空间
//todo:没有c#的代码添加上去。修改结构,添加一个尾部节点地址的指针
c语言实现:
因为链表中的每个节点都是使用malloc()动态创建的,所以需要调用free()释放待删除节点占用的内存空间。
// A complete working C program to demonstrate deletion in singly // linked list #include <stdio.h> #include <stdlib.h> // A linked list node struct Node { int data; struct Node *next; }; /* Given a reference (pointer to pointer) to the head of a list and an int, inserts a new node on the front of the list. */ void push(struct Node** head_ref, int new_data) { struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); new_node->data = new_data; new_node->next = (*head_ref); (*head_ref) = new_node; } /* Given a reference (pointer to pointer) to the head of a list and a key, deletes the first occurrence of key in linked list */ void deleteNode(struct Node **head_ref, int key) { // Store head node struct Node* temp = *head_ref, *prev; // If head node itself holds the key to be deleted if (temp != NULL && temp->data == key) { *head_ref = temp->next; // Changed head free(temp); // free old head return; } // Search for the key to be deleted, keep track of the // previous node as we need to change 'prev->next' while (temp != NULL && temp->data != key) { prev = temp; temp = temp->next; } // If key was not present in linked list if (temp == NULL) return; // Unlink the node from linked list prev->next = temp->next; free(temp); // Free memory } // This function prints contents of linked list starting from // the given node void printList(struct Node *node) { while (node != NULL) { printf(" %d ", node->data); node = node->next; } } /* Drier program to test above functions*/ int main() { /* Start with the empty list */ struct Node* head = NULL; push(&head, 7); push(&head, 1); push(&head, 3); push(&head, 2); puts("Created Linked List: "); printList(head); deleteNode(&head, 1); puts("\nLinked List after Deletion of 1: "); printList(head); return 0; }
因为涉及到遍历,时间复杂度O(n)
输出
Created Linked List: 2 3 1 7 Linked List after Deletion of 1: 2 3 7
java:
// A complete working Java program to demonstrate deletion in singly // linked list class LinkedList { Node head; // head of list /* Linked list Node*/ class Node { int data; Node next; Node(int d) { data = d; next = null; } } /* Given a key, deletes the first occurrence of key in linked list */ void deleteNode(int key) { // Store head node Node temp = head, prev = null; // If head node itself holds the key to be deleted if (temp != null && temp.data == key) { head = temp.next; // Changed head return; } // Search for the key to be deleted, keep track of the // previous node as we need to change temp.next while (temp != null && temp.data != key) { prev = temp; temp = temp.next; } // If key was not present in linked list if (temp == null) return; // Unlink the node from linked list prev.next = temp.next; } /* Inserts a new Node at front of the list. */ public void push(int new_data) { Node new_node = new Node(new_data); new_node.next = head; head = new_node; } /* This function prints contents of linked list starting from the given node */ public void printList() { Node tnode = head; while (tnode != null) { System.out.print(tnode.data+" "); tnode = tnode.next; } } /* Drier program to test above functions. Ideally this function should be in a separate user class. It is kept here to keep code compact */ public static void main(String[] args) { LinkedList llist = new LinkedList(); llist.push(7); llist.push(1); llist.push(3); llist.push(2); System.out.println("\nCreated Linked list is:"); llist.printList(); llist.deleteNode(1); // Delete node at position 4 System.out.println("\nLinked List after Deletion at position 4:"); llist.printList(); } }
获取链表长度(迭代法和递归法)
迭代法
1、设定一个计数器,初始值为0
2、初始化current到头节点
3、如果current不为null进行以下循环
a) current = current -> next b) count++;4、返回计数器c语言:
/* Counts no. of nodes in linked list */ int getCount(struct Node* head) { int count = 0; // Initialize count struct Node* current = head; // Initialize current while (current != NULL) { count++; current = current->next; } return count; }
java:
/* Returns count of nodes in linked list */ public int getCount() { Node temp = head; int count = 0; while (temp != null) { count++; temp = temp.next; } return count; }
c#:
/* Returns count of nodes in linked list */ public int getCount() { Node temp = head; int count = 0; while (temp != null) { count++; temp = temp.next; } return count; }
递归法
int getCount(head) 1) If head is NULL, return 0. 2) Else return 1 + getCount(head->next) c语言:
/* Counts the no. of occurrences of a node (search_for) in a linked list (head)*/ int getCount(struct Node* head) { // Base case if (head == NULL) return 0; // count is 1 + count of remaining list return 1 + getCount(head->next); }
java
/* Returns count of nodes in linked list */ public int getCountRec(Node node) { // Base case if (node == null) return 0; // Count is this node plus rest of the list return 1 + getCountRec(node.next); }
c#
/* Returns count of nodes in linked list */ public int getCountRec(Node node) { // Base case if (node == null) return 0; // Count is this node plus rest of the list return 1 + getCountRec(node.next); }
在链表中查找元素
函数签名:
bool search(Node *head, int x)
如果在链表中查找到这个元素返回true,否则false
迭代法
2) 初始化一个节点指针, current = head. 3) 如果current不为NULL执行以下循环 a) current->key 等于当前待查找值key则返回true. b) current = current->next 4) 否则返回 false
/*Checks whether the value key is present in linked list*/ bool search(struct Node* head, int key) { struct Node* current = head; //Initialize current while(current != NULL) { if(current->data == key) return true; current = current->next; } return false; }
java:
//Checks whether the value key is present in linked list public boolean search(Node head, int key) { Node current = head; //Initialize current while (current != null) { if (current.data == key) return true; //data found current = current.next; } return false; //data not found }
c#
// Checks whether the value key is present in linked list public bool search(Node head, int key) { Node current = head; // Initialize current while (current != null) { if (current.data == key) return true; // data found current = current.next; } return false; // data not found }
递归法:
bool search(head, x) 1)如果head是NULL, return false. 2) 如果head数据域的值和待查找的一致,返回true; 2) 否则返回 search(head->next, x)
c语言:
/* Checks whether the value key is present in linked list */ bool searchRecursive(struct Node* head, int key) { if(head == NULL) { return false; } //If key is present in current node, //return true if(head->data == key) return true; //recursive for remaining list return searchRecursive(head->next, key); }
java:
// Checks whether the value key is present // in linked list public boolean search(Node head, int key) { // Base case if (head == null) return false; // If key is present in current node, // return true if (head.data == key) return true; // Recur for remaining list return search(head.next, key); }
c#
// Checks whether the value key is present // in linked list public bool search(Node head, int key) { // Base case if (head == null) return false; // If key is present in current node, // return true if (head.data == key) return true; // Recur for remaining list return search(head.next, key); }