数据结构-线性表-头指针&头结点

本文深入探讨了链表中的头结点概念,解释了头结点的作用与优势,包括如何通过头结点实现链表操作的统一与方便,特别强调了头结点在插入、删除操作中的应用,并对比了有无头结点时操作的差异。

链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。

这里有个地方要注意,就是对头指针概念的理解,这个很重要。

“链表中第一个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针

画一个图吧。


这个图看起来很清晰了。比如说头结点,我们就可以这么描述了:

  • 头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等)。
  • 有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。
  • 首元结点也就是第一个元素的结点,它是头结点后边的第一个结点。
  • 头结点不是链表所必需的。
是的,对于 头指针,我们也可以有相应的理解了。

  • 在线性表的链式存储结构中,头指针是指链表指向第一个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针。
  • 头指针具有标识作用,故常用头指针冠以链表的名字。
  • 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
单链表也可以没有头结点。如果没有头结点的话,那么单链表就会变成这样:



说了这么多,既然头结点可有可无,那到底是该有还是不该有呢?

总得来说,一般使用线性表,都是指有头结点得情况。

下面简单总结了两个使用头结点优点:

  头结点是在链表的开始结点之前附加一个结点。它具有两个优点:
 ⒈由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作就和在表的其它位置上操作一致,无须进行特殊处理;
 ⒉无论链表是否为空,其头指针都是指向头结点的非空指针(空表中头结点的指针域空),因此空表和非空表的处理也就统一了。

方便在第1个位置进行插入、删除操作时,同其他位置一样。

加了头结点之后,插入、删除都是在后继指针next上进行操作,不用动头指针;

若不加头结点的话,在第1个位置插入或者删除第1个元素时,需要动的是头指针。

例如:在进行删除操作时,L为头指针,p指针指向被删结点,q指针指向被删结点的前驱,对于非空的单链表:
1.带头结点时
删除第1个结点(q指向的是头结点):q->next=p->next; free(p);
删除第i个结点(i不等于1):q->next=p->next;free(p);
2.不带头结点时
删除第1个结点时(q为空):L=p->next; free(p);
删除第i个结点(i不等于1):q->next=p->next;free(p);
结论:带头结点时,不论删除哪个位置上的结点,用到的代码都一样;

不带头结点时,删除第1个元素和删除其它位置上的元素用到的代码不同,相对比较麻烦。

### Java 线性表数据结构设计与实现 #### 一、线性表概述 线性表是一种基本的数据结构,其特点是元素之间存在一对一的关系。在线性表中,除了第一个和最后一个元素外,其他每个元素都有唯一的一个前驱和后继。 #### 二、顺序表实现 顺序表是线性表的一种存储方式,在内存中占用连续的空间来保存各个节点的信息。下面是一个简单的顺序表示例: ```java public class ArrayLinearList { private Object[] elements; private int size; public ArrayLinearList() { this.elements = new Object[10]; this.size = 0; } // 添加元素到列表末尾 public boolean add(Object element) { ensureCapacity(size + 1); elements[size++] = element; return true; } // 获取位置上的元素 public Object get(int index) { checkIndex(index, false); return elements[index]; } // 移除并返回定索引处的元素 public Object remove(int index){ rangeCheck(index); Object oldValue = elements[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elements, index+1, elements, index, numMoved); elements[--size] = null; // Let gc do its work return oldValue; } // 扩展数组容量以容纳更多元素 private void ensureCapacity(int minCapacity) { if(minCapacity >= elements.length){ @SuppressWarnings("unchecked") Object[] oldElements = elements; elements = new Object[Math.max(2 * elements.length,minCapacity)]; System.arraycopy(oldElements, 0, elements, 0,oldElements.length ); } } // 检查给定索引是否有效 private void checkIndex(int index,boolean isAddOperation){ if(isAddOperation &&index>size || !isAddOperation&&index>=size||index<0){ throw new IndexOutOfBoundsException(); } } } ``` 此代码展示了如何创建一个基于数组的顺序表类 `ArrayLinearList`[^1]。 #### 三、链表实现 链表也是一种常见的线性表实现形式,它由一系列结点组成,这些结点不必在物理上相邻存放在一起;而是通过指针链接起来形成逻辑上的序列关系。以下是单向链表的具体实现: ```java class Node<T>{ T data; Node next; public Node(T data){ this.data=data; this.next=null; } } public class SinglyLinkedList<T> { private Node head; private int length; public SinglyLinkedList(){ this.head=null; this.length=0; } // 向链表部插入新节点 public void insertAtBeginning(T newData){ Node newNode=new Node(newData); newNode.next=head; head=newNode; length++; } // 删除节点 public T deleteFromBeginning(){ if(head==null)return null; T temp=(T)(head.data); head=head.next; length--; return temp; } // 显示整个链表的内容 public String toString(){ StringBuilder sb=new StringBuilder("["); Node current=head; while(current!=null){ sb.append(current.data.toString()); if(current.next != null)sb.append(", "); current=current.next; } sb.append("]"); return sb.toString(); } } ``` 这段代码定义了一个泛型化的单向链表类 `SinglyLinkedList`, 并实现了几个常用的操作函数[^2]. 对于双向链表,则是在上述基础上增加了对前后两个方向的支持,即每个节点不仅有向下一个节点的引用(`next`)还有向前一个节点的引用(`prev`). 这样可以更方便地遍历链表以及执行某些特定操作. 最后需要注意的是,无论是哪种类型的线性表,在实际应用过程中都需要考虑边界条件处理(比如越界访问)、异常情况下的健壮性和性能优化等问题.
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Colin丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值