详谈单链表的有关操作集锦~

1.单链表

   在 Java 中没有显式的指针类型,然而实际上对象的访问就是使用指针来实现的,即在Java 中是使用对象的引用来替代指针的。因此在使用 Java 实现该结点结构时,一个结点本身就是一个对象。结点的数据域 data 可以使用一个 Object 类型的对象来实现,用于存储任何类型的数据元素,并通过对象的引用指向该元素;而指针域 next 可以通过节点对象的引

用来实现。  

   单链表结点结构是结点的一种最简单的形式,除此之外还有其他不同的结点结构,但是这些结点结构都有一个数据域,并均能完成数据元素的存取。为此在使用 Java 定义单链表结点结构之前先给出一个结点接口,在接口中定义了所有结点均支持的操作,即对结点中存储数据的存取。

   结点接口

public interface Node {
    //获取结点数据域
    public Object gerData();
    //设置结点数据域
    public void setData(Object obj);
}
   单链表结点定义
public class SLNode implements Node {
    public Object element;
    public SLNode next;
    public SLNode() {
        this(null, null);
    }
    public SLNode(Object ele, SLNode next) {
        this.element = ele;
        this.next = next;
    }
    public SLNode getNext() {
        return next;
    }
    public void setNext(SLNode next) {
        this.next = next;
    }
    /** ************** Methods of Node Interface ************* */
    public Object gerData() {
        return element;
    }
    public void setData(Object obj) {
       element = obj;
    }
} 

单链表结构

链表的第一个结点和最后一个结点,分别称为链表的首结点和尾结点。尾结点的特征是其 next 引用为空( null)。链表中每个结点的 next 引用都相当于一个指针,指向另一个结点,借助这些 next 引用,我们可以从链表的首结点移动到尾结点。在单链表中,经常用head引用来指向链表的首结点,由head引用可以完成对链表所有结点的访问,当然有时候也会用指向尾结点的tail引用来方便完成某些操作的实现。

需要注意的是:在单链表结构中还需要注意的一点是,由于每个结点的数据域都是一个
Object 类的对象。

与数组类似,单链表中的结点也具有一个线性次序,即如果结点 P 的 next 引用指向结点 S,则 P 就是 S 的直接前驱, S 是 P
的直接后续。单链表的一个重要特性就是只能通过前驱结点找到后续结点,而无法从后续结点找到前驱结点。在单链表中通常需要完成数据元素的查找、插入、删除等操作。

(1)查找:在单链表中进行查找操作,只能从链表的首结点开始,通过每个结点的 next 引用来一次访问链表中的每个结点以完成相应的查找操作。

在单链表中进行查找操作,只能从链表的首结点开始,通过每个结点的 next 引用来一次访问链表中的每个结点以完成相应的查找操作。

除了单链表的首结点由于没有直接前驱结点,所以可以直接在首结点之前插入一个新的结点之外,在单链表中的其他任何位置插入一个新结点时,都只能是在已知某个特定结点引用的基础上在其后面插入一个新结点。并且在已知单链表中某个结点

引用的基础上,完成结点的插入操作需要的时间是Θ(1)。由于在单链表中数据元素的插入是通过节点的插入来完成的,因此在单链表中完成数据元素的插入操作要比在数组中完成数据元素的插入操作所需Ο(n)的时间要快得多。

(3)删除

链表的删除和插入相似,数据元素的删除也是通过对结点的操作完成的。在不同的位置,操作会略有不同。

在单链表中删除一个结点时,除首结点外都必须知道该结点的直接前驱结点的引用。并且在已知单链表中某个结点引用的基础上,完成其后续结点的删除操作需要的时间是Θ
(1)。由于在单链表中数据元素的删除是通过节点的删除来完成的,因此

在单链表中完成数据元素的删除操作要比在数组中完成数据元素的删除操作所需Ο (n)的时间要快得多。

(4)结论:在单链表中进行顺序查找与在数组中完成相同操作具有相同的时间复杂度,而在单链表中在已知特定结点引用的前提下完成数据元素的插入与删除操作要比在数组中完成相同操作快得多。

<strong>2.单链表的实现
class LinkList {
 private Node head = null;
 private Node tail = null;
 int Length = 0;
 public Node getHead() {
  return head;
 }
 public void setHead(Node head) {
  this.head = head;
 }
 public Node getTail() {
  return tail;
 }
 public void setTail(Node tail) {
  this.tail = tail;
 }
 // 判断链表是否为空
 public boolean isEmpty() {
  return (Length == 0);
 }
 // 查找指定位置的结点
 public Node FindKth(int k) {
  Node temp = head;
  int i = 1;
  while (temp != null && i < k) { // 移动元素,直到找到相应位置
   temp = temp.next;
   i++;
  }
  if (i == k)
   return temp;
  else
   return null;
 }
 // 查找指定元素所在的位置
 public int Find(String str) {
  Node temp = head;
  int current = 1;
  while (temp != null && temp.key.equals(str)) {
   temp = temp.next;
   current++;
  }
  if (temp.key.equals(str))
   return current;
  else
   return -1;
 }
 // 头插法
 public void insertHead(String str) {
  Node newLink = new Node(str);
  newLink.next = head;
  head = newLink;
  Length++;
 }
 // 将结点插入指定位置
 public void insert(int index, String str) {
  Node newLink = new Node(str);
  if (index > 1 && index < Length) {
   newLink.next = FindKth(index - 1).next;
   FindKth(index - 1).next = newLink;
   // System.out.println(head.key); 此时head的值为4
   Length++;
  } else if (index == 1) {
   newLink.next = head;
   head = newLink;
   Length++;
  } else {
   System.out.println("超出链表长度,插入无效");
  }
 }
 // 删除指定位置的结点
 public void delete(int index) {
  if (index == 1) { // 删除位置为头结点
   head = head.next;
   Length--;
  } else if (index > 1 && index < Length) { // 删除位置不为头结点或尾结点
   Node temp = FindKth(index).next;
   FindKth(index - 1).next = temp;
   Length--;
  } else {
   System.out.println("超出链表长度,查找无效");
  }
 }
 // 删除指定元素
 public void deleteNode(String str) {
  int current = Find(str);
  if (current > 1 && current < Length) { // 删除的位置为头结点
   Node temp = FindKth(current).next;
   FindKth(current - 1).next = temp;
   Length--;
  } else if (current == 1) {
   head = head.next;
   Length--;
  } else if (current == -1) {
   System.out.println("要删除的元素不存在,请重新选择!");
  }
 }
 // 初始化链表
 public void initList(Node node) {
  head = node;
  head.next = tail;
 }
 // 打印单链表
 public void display() {
  Node current = head;
  while (current != null) {
   current.displayNode();
   current = current.next;
  }
 }
}
public class NodeTest {
 public static void main(String args[]) {
  LinkList list = new LinkList();
  list.insertHead("1");
  list.insertHead("2");
  list.insertHead("3");
  list.insertHead("4");
  System.out.println("头插法操作后的单链表:");
  list.display();
  System.out.println();
  // 把元素插入指定位置
  int index = 1;
  System.out.println("插入元素后的单链表:");
  list.insert(index, "a");
  list.display();
  // 删除元素
  System.out.println();
  System.out.println("删除元素后的单链表(删除指定元素):");
  list.deleteNode("7");
  list.display();
  // 删除元素
  System.out.println();
  System.out.println("删除元素后的单链表(删除结点位置):");
  list.delete(4);
  list.display();
 }
}
class Node {
 public String key;
 public Node next;// 指向下一个元素的指针
 // 初始化头结点
 public Node(String str) {
  this.key = str;
  this.next = null;
 }
 public void displayNode() {
  System.out.print(key + " -> ");
 }
}</strong>

3.基于时间的比较

  线性表有查找,删除,插入三类操作。

  对于查找操作有基于序号的查找,即存取线性表中 i 号数据元素。由于数组有随机存取的特性,在线性表的顺序存储实现中可以在Θ(1)的时间内完成;而在链式存储中由于需要从头结点开始顺着链表才能取得,无法在常数时间内完成,因此顺序存储优于链式存储。查找操作还有基于元素的查找,即线性表是否包含某个元素、元素的序号是多少,这类操作线性表的顺序存储与链式存储都需要从线性表中序号为 0 的元素开始依次查找,因此两种实现的性能相同。综上所述,如果在线性表的使用中主要操作是查找,那么应当选用顺序存储实现的线性表。

对于基于数据元素的插入、删除操作而言,当使用数组实现相应操作时,首先需要采用顺序查找定位相应数据元素,然后才能插入、删除,并且在插入、删除过程又要移动大量元素;相对而言链表的实现只需要在定位数据元素的基础上,简单的修改几个指针即可完成,因此链式存储优于顺序存储。对于基于序号的插入、删除操作,因为在顺序存储中平均需要移动一半元素;而在链式存储中不能直接定位,平均需要比较一半元素才能定位。因此顺序存储与链式存储性能相当。综上所述,如果在线性表的使用中主要操作是插入、删除操作,那么选用链式存储的线性表为佳。

4.基于空间的比较  

   线性表的顺序存储,其存储空间是预先静态分配的,虽然在实现的过程中可以动态扩展数组空间,但是如果线性表的长度变化范围较大,空间在使用过程中由于会存在大量空闲空间,使得存储空间的利用率不高。而线性表的链式存储,其结点空间是动态分配的,不会存在存储空间没有完全利用的情况。因此当线性表长度变化较大时,宜采用链式存储结构。当线性表的数据元素结构简单,并且线性表的长度变化不大时。由于链式存储结构使用了额外的存储空间来表示数据元素之间的逻辑关系,因此针对数据域而言,指针域所占比重较大;而在线性表的顺序存储结构中,没有使用额外的存储空间来表示数据元素之间的逻辑关系,尽管有一定的空闲空间没有利用,但总体而言由于线性表长度变化不大,因此没有利用的空间所占比例较小。所以当线性表数据元素结构简单,长度变化不大时可以考虑采用顺序存储结构。

时间: 2024-10-18 09:33:06

详谈单链表的有关操作集锦~的相关文章

单链表的基础操作

单链表中节点的查找.插入.删除.求单链表长度等操作. 按序号查找结点值 在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL. 按序号查找结点值的算法如下: LNode GetElem(LinkList L,int i){ //本算法取出单链表L(带头结点)中第i个位置的结点指针 int j=1; //计数,初始为1 LNode *p = L->next; //头结点指针赋给p if(i==0) return L; //若i等于0,

单链表的相关操作

#ifndef _SLIST_H #define _SLIST_H #ifdef __cplusplus extern "C" { #endif /*******1. 不带头结点的单链表*****/ /***** *@链表结点结构定义 *@ m_data:数据 *@m_pNext:指向下一结点的指针 ***/ struct listNode { int m_data; listNode* m_pNext; }; /******* *@ 用数组array初始化链表,数组元素个数为n *@

数据结构关于单链表的一些操作的源代码

单链表的可以有许多问题,这是我特意整理一下的有关他的相关操作,给出代码,有需要的可以自己调试,重要的就是关于环的一些操作: #include <iostream>#include <cstdio>#include <cstdlib>#include <ctime>using namespace std;typedef int Elemtype;typedef struct Node{ Elemtype data; struct Node *next;}Nod

算法数据结构 单链表的实现+操作 以及和顺序表的对比

顺序表和单链表的优缺点对比: 顺序表的优点,无需为表示表中元素之间的逻辑关系而增加额外的存储空间: 可以快速的存取表中的任意位置的元素. 顺序表的缺点,插入后删除操作需要移动大量元素: 当线性表长度不稳定时,存储空间难确定,容易造成存储空间碎片. 对于单链表 链式存储即元素存储的内存单元可以是不连续,分散的.对于元素间如何来维护他们的关系(即逻辑结构,每个元素的前驱和后继.) 即用到一个指针域来存储他和前驱或是后继直接的关系. 如上面的是一个单链表的指针结构,即每个元素中存储了他的后继元素的内存

链表-单链表的各种操作

单链表的结构体的定义 typedef struct LNode { ElemType data; struct LNode *next; }LinkList; 基本的单链表的操作 /* 功能:构建一个空的带头节点的单链表*/ Status InitList (struct LNode **L) { (*L) = (struct LNode *)malloc(sizeof(struct LNode)); //产生头节点 if(!*L) exit(OVERFLOW); (*L)->next = NU

单链表的常用操作(二)

接上一篇单链表的基本操作,我又整理了一些链表常考的题目,并且不断更新中... 1.查找链表中倒数第k个节点以及删除倒数第k个节点 //给两个指针p和q,让其中一个指针p领先q指针k步, //然后再同时移动p和q指针,当领先的指针p先到达链表尾部时,后面的指针q所指向的节点恰好为倒数第k个节点. Node* GetKthNode(int k) { Node *p=head; Node *q=head; while(k>1&&p->next!=NULL) { p=p->nex

带头节点的单链表的插入操作

1.偶然看到了十字链表的应用,想到之前在<数据结构与算法分析>的链表一章中,需要用多重表实现一个简单的查询功能.功能需求如下: “已知 学生 和 学校课程 总数 分别为 40000 和 2500,现在需要得到两份报告,一份显示每门课成注册的所有学生信息, 一份显示每个学生注册了哪些课程.” 显然可以用一个 40000 * 2500 个元素的二维数组来解决,但是每个学生选课数目很少,因此会浪费很多空间.因此选择十字链表来实现. 既然是链表,那么肯定要有插入操作,于是便有了本文.算是对功能实现前的

单链表逆序操作

//逆序操作//算法1:从第二个节点开始,记录它的下一个节点,然后依次挪到第一个节点之前成为新表头int inverse_node(struct node pH){struct node p = pH; //头结点struct node pPrev = NULL; //记录前一个节点struct node pBack = NULL; //记录下一个节点地址struct node *pFirstNode = p->pNext; //记录第一个节点 //节点只有1个或者无有效节点时,返回原来的链表,

单链表的各种操作

#include <stdio.h> #include <stdlib.h> typedef struct { char data; struct Node * next; }Node, *LinkList; void meau(); LinkList CreateFromHead(); void ListLength(LinkList L); void printLink(LinkList L); LinkList inversePermutation(LinkList L);