相关基础知识补充
指针:表示一个数据元素逻辑意义上的存储位置。
Java语言用通过对象的引用来表示指针。通过把新创建对象赋值给一个对象引用,即是让该对象引用表示(或指向)了所创建的对象。
链式存储结构是基于指针实现的。我们把一个数据元素和一个指针称为一个结点。链式存储结构是用指针把相互直接关联的结点(即直接前驱结点和直接后继结点)链接起来。链式存储结构的特点是数据元素之间的逻辑关系表现在结点的链接关系上。链式存储结构的线性表为链表。
根据结点构造链的方法不同,链表主要有单链表、单循环链表和双循环链表。
单链表的结构:
单链表中,构成链表的每个结点只有一个指向后继结点的指针。
单链表有带头结点结构和不带头结点结构两种:
我们把指向单链表的指针称为单链表的头指针。头指针所指的是不存放数据元素的第一个结点称为头结点。存放第一个数据元素的结点称为第一个数据元素结点,或称为首元结点。
顺序存储结构与链式存储结构的差别?
在顺序存储结构中,用户向系统申请一块地址连续的有限空间用于数据存储数据元素序列,这样任意逻辑上相邻的数据元素在物理存储位置上也必然相邻。
在链式存储结构中,由于链式存储结构在初始化时为空,每当有新数据元素需要存储时,用户才向系统动态申请的结点插入链表中,而这些在不同时刻向系统申请的结点,一般情况下其存储位置并不连续。因此,在链式存储结构中,任意两个逻辑上相邻的数据元素在物理上不一定相邻,数据元素的逻辑关系是通过指针链接实现的。
源代码:
package com.algorithm;
//单链表实列化
public class SingleLinkedList<T> {
/*
* 结点类
*/
private static class Node<T> {
T nodeValue; // 数据域
Node<T> next; // 指针域保存着下一节点的引用
Node(T nodeValue, Node<T> next) {
this.nodeValue = nodeValue;
this.next = next;
}
Node(T nodeValue) {
this(nodeValue, null);
}
}
// 单链表相关属性和方法
private Node<T> head, tail;
// 构造函数
public SingleLinkedList() {
head = tail = null;
}
// 判断单链表是否为空
public boolean isEmpty() {
return head == null;
}
/**
* 创建头指针,该方法只用一次!
*/
public void addToHead(T item) {
head = new Node<T>(item);
if (tail == null)
tail = head;
}
/**
* 添加尾指针,该方法使用多次
*/
public void addToTail(T item) {
if (!isEmpty()) { // 若链表非空那么将尾指针的next初使化为一个新的元素
tail.next = new Node<T>(item); // 然后将尾指针指向现在它自己的下一个元素
tail = tail.next;
} else { // 如果为空则创建一个新的!并将头尾同时指向它
head = tail = new Node<T>(item);
}
}
/**
* 打印列表
*/
public void printList() {
if (isEmpty()) {
System.out.println("null");
} else {
for (Node<T> p = head; p != null; p = p.next)
System.out.println(p.nodeValue);
}
}
/**
* 在表头插入结点,效率非常高
*/
public void addFirst(T item) {
Node<T> newNode = new Node<T>(item);
newNode.next = head;
head = newNode;
}
/**
* 在表尾插入结点,效率很低
*/
public void addLast(T item) {
Node<T> newNode = new Node<T>(item);
Node<T> p = head;
while (p.next != null)
p = p.next;
p.next = newNode;
newNode.next = null;
}
/**
* 在表头删除结点,效率非常高
*/
public void removeFirst() {
if (!isEmpty())
head = head.next;
else
System.out.println("The list have been emptied!");
}
/**
* 在表尾删除结点,效率很低
*/
public void removeLast() {
Node<T> prev = null, curr = head;
while (curr.next != null) {
prev = curr;
curr = curr.next;
if (curr.next == null)
prev.next = null;
}
}
/**
* <p>
* 插入一个新结点
* </p>
* <ul>
* 插入操作可能有四种情况:
* <li>①表为空, 返回false</li>
* <li>②表非空,指定的数据不存在</li>
* <li>③指定的数据是表的第一个元素</li>
* <li>④指定的数据在表的中间</li>
* </ul>
*
* @param appointedItem
* 指定的nodeValue
* @param item
* 要插入的结点
* @return 成功插入返回true;
*/
public boolean insert(T appointedItem, T item) {
Node<T> prev = head, curr = head.next, newNode;
newNode = new Node<T>(item);
if (!isEmpty()) {
while ((curr != null) && (!appointedItem.equals(curr.nodeValue))) { // 两个判断条件不能换
prev = curr;
curr = curr.next;
}
newNode.next = curr; // ②③④
prev.next = newNode;
return true;
}
return false; // ①
}
/**
* <p>
* 移除此列表中首次出现的指定元素
* </p>
* <ul>
* 删除操作可能出现的情况:
* <li>①prev为空,这意味着curr为head. head = curr.next; --> removeFirst();</li>
* <li>②匹配出现在列表中的某个中间位置,此时执行的操作是 --> prev.next = curr.next;,</li>
* </ul>
* <p>
* 在列表中定位某个结点需要两个引用:一个对前一结点(prev左)的引用以及一个对当前结点(curr右)的引用.
* </p>
* prev = curr; curr = curr.next;
*/
public void remove(T item) {
Node<T> curr = head, prev = null;
boolean found = false;
while (curr != null && !found) {
if (item.equals(curr.nodeValue)) {
if (prev == null)
removeFirst();
else
prev.next = curr.next;
found = true;
} else {
prev = curr;
curr = curr.next;
}
}
}
/**
* 返回此列表中首次出现的指定元素的索引,如果列表中不包含此元素,则返回 -1.
*/
public int indexOf(T item) {
int index = 0;
Node<T> p;
for (p = head; p != null; p = p.next) {
if (item.equals(p.nodeValue))
return index;
index++;
}
return -1;
}
/**
* 如果此列表包含指定元素,则返回 true。
*/
public boolean contains(T item) {
return indexOf(item) != -1;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SingleLinkedList<String> t = new SingleLinkedList<String>();
//单链表添加头指针
t.addFirst("A");
//单链表添加相关数据
t.insert("B", "c");
t.insert("c", "c++");
t.insert("d", "java");
t.insert("e", "c#");
t.insert("f", "php");
//单链表添加尾指针
t.addLast("G");
//单链表移除
// t.remove("c");
//移除尾结点
t.removeLast();
//移除头结点
t.removeFirst();
boolean target=t.contains("ruby");
System.out.println("是否存在指定元素:"+target);
t.printList(); // A B C
}
}