链表长度计算方法:详解如何高效获取链表长度
链表是什么?
- 链表是一种用于存储数据集合的线性数据结构
 - 连续的元素通过指针连接
 - 最后一个元素指向NULL(空值)
 - 每个元素是一个独立的对象,称为节点(Node)
 - 链表中的每个节点包含两部分:数据和指向下一个节点的引用
 


如何找到链表的长度?
找到链表的长度有两种方法。
- 迭代方法
 - 递归方法
 
使用迭代方法计算链表的长度
我们将使用链表遍历来找到链表的长度。
- 头节点(Head)指向链表的第一个节点
 - 将计数变量(count)初始化为0
 - 将临时变量(temp)初始化为头节点(Head)
 - 每访问一个节点,计数变量的值就增加1
 - 当到达空值(null)时停止过程
 - 不要改变头节点的引用
 

用Java编码
package com.Olivia.ds;
public class MyLinkedList {
	public class Node {
		int data;
		Node next;
	}
	public Node head;
	public Node tail;
	public int size;
	public int getFirst() throws Exception {
		if (this.size == 0) {
			throw new Exception("链表为空");
		}
		return this.head.data;
	}
	public int getLast() throws Exception {
		if (this.size == 0) {
			throw new Exception("链表为空");
		}
		return this.tail.data;
	}
	public void display() {
		Node temp = this.head;
		while (temp != null) {
			System.out.println(temp.data + " ");
			temp = temp.next;
		}
	}
	public void addFirst(int item) {
		Node nn = new Node();
		nn.data = item;
		if (this.size == 0) {
			this.head = nn;
			this.tail = nn;
			this.size = this.size + 1;
		} else {
			nn.next = this.head;
			this.head = nn;
			this.size = this.size + 1;
		}
	}
	public int length() {
		Node temp = this.head;
		int count = 0;
		while (temp != null) {
			count++;
			temp = temp.next;
		}
		return count;
	}
	public static void main(String[] args) {
		MyLinkedList ll = new MyLinkedList();
		ll.addFirst(10);
		ll.addFirst(20);
		ll.addFirst(30);
		ll.addFirst(40);
		ll.addFirst(50);
		System.out.println("链表长度为 " + ll.length());
	}
}
在C语言中编码
#include <stdio.h>
#include <stdlib.h>
/* 链表节点的结构 */
struct node {
  int data;
  struct node *next;
} *head;
void initialize(){
    head = NULL;
}
/*
在单链表前面插入一个节点
*/
void insert(int num) {
    /* 创建一个新的链表节点 */
    struct node* newNode = (struct node*) malloc(sizeof(struct node));
    newNode->data  = num;
    /* 新节点的下一个指针将指向链表的头节点 */
    newNode->next = head;
    /* 使新节点成为链表的新头节点 */
    head = newNode;
    printf("插入的元素 : %d\n", num);
}
int getLength(struct node *head){
    int length =0;
    while(head != NULL){
        head = head->next;
        length++;
    }
    return length;
}
/*
从头节点到尾节点打印链表
*/
void printLinkedList(struct node *nodePtr) {
  while (nodePtr != NULL) {
     printf("%d", nodePtr->data);
     nodePtr = nodePtr->next;
     if(nodePtr != NULL)
         printf("-->");
  }
}
int main() {
    initialize();
    /* 创建一个链表 */
    insert(8); 
    insert(3);
    insert(2);
    insert(7);
    insert(9);
    printf("\n链表\n");
    printLinkedList(head);
    printf("\n链表长度 : %d", getLength(head));
    return 0;
}
结果

使用递归解决方案计算链表的长度
基础情况:
- 最后一个节点指向空值(Null)
 - 返回0
 
递归情况:
- 在每一步,将当前节点的值更新为下一个节点
 - 调用 = 1+fun(curr.next)
 

在链表中有3个元素:LL1、LL2和LL3。当进行递归调用时,我们将观察内存堆栈中发生的情况。内存堆栈:

主函数调用LL1,LL1调用LL2,LL2调用LL3,LL3调用空值。当达到空值时,我们从这里返回。0被返回给LL3,LL3向LL2返回1,LL2向LL1返回2,最后LL1向主函数返回3。
使用Java进行编码
Java实现递归方法计算链表长度
下面是一个完整的Java实现,展示了如何使用递归方法计算链表的长度。我们首先定义了一个链表类,其中包含节点类、头节点、尾节点和大小属性。
package com.Olivia.ds;
public class MyLinkedList {
    public class Node {
         int data;
         Node next;
    }
    public Node head;
    public Node tail;
    public int size;
    public int getfirst() throws Exception {
         if (this.size == 0) {
             throw new Exception("链表为空");
         }
         return this.head.data;
    }
    public int RemoveFirst() throws Exception {
         if (this.size == 0) {
             throw new Exception("链表为空");
         }
         Node temp = this.head;
         if (this.size == 1) {
             this.head = null;
             this.tail = null;
             size = 0;
         } else {
             this.head = this.head.next;
             this.size--;
         }
         return temp.data;
    }
    public void addFirst(int item) {
         Node nn = new Node();
         nn.data = item;
         if (this.size == 0) {
             this.head = nn;
             this.tail = nn;
             this.size = this.size + 1;
         } else {
             nn.next = this.head;
             this.head = nn;
             this.size = this.size + 1;
         }
    }
    public int lengthUsingRecursiveApproach (){
         return lengthUsingRecursiveApproach(this.head);
    }
    private int lengthUsingRecursiveApproach(Node curr) {
         // 自动生成的方法存根
         if (curr == null) {
             return 0;
         }
         return 1 + lengthUsingRecursiveApproach (curr.next);
    }
    public static void main(String[] args) {
         MyLinkedList ll = new MyLinkedList();
         // 向链表中插入元素
        ll.addFirst(10);
         ll.addFirst(20);
         ll.addFirst(30);
         ll.addFirst(40);
         ll.addFirst(50);
         // 链表长度
         System.out.println("递归方法计算链表长度 " + ll.lengthUsingRecursiveApproach(ll.head));
    }
}
C语言实现递归方法计算链表长度
接下来,我们看看如何在C语言中实现递归方法来计算链表的长度。C语言实现需要手动管理内存,使用malloc函数为节点分配内存空间。
#include <stdio.h>
struct Node
{
    int data;
    struct Node* next;
};
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;
}
int getCount(struct Node* head)
{
    // 基本情况
    if (head == NULL)
        return 0; 
    return 1 + getCount(head->next);
}
int main()
{
    struct Node* head = NULL;
    push(&head, 1);
    push(&head, 3);
    push(&head, 1);
    push(&head, 2);
    push(&head, 1);
    printf("节点数量为 %d", getCount(head));
    return 0;
}
输出结果

时间复杂度分析
在递归和迭代解决方案中,时间复杂度均为O(N),因为我们只需要进行一次遍历来得知链表的长度。递归方法的优点是代码简洁,但缺点是可能会因为递归深度过大而导致栈溢出,特别是对于非常长的链表。在实际应用中,应根据链表的可能长度和系统资源情况选择合适的方法。