链表常用算法 java_Java数据结构算法(一)链表

e041f289d481

本文旨作于收集整理使用,在学习链表 之前,需自行回顾数组的知识点。

导航

一、链表

链表是一种物理存储单元上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

1.1 单向链表

e041f289d481

链表

一个单向链表是由许多的节点组成的,每个节点由数据域:保存或者显示节点信息,指针域:存储下一个节点的地址和保存数据的数据域组成。

单向链表只可向一个方向遍历:

查询一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。

插入一个节点,对于单向链表,我们需要将插入位置的前节点的指针指向自己,而自己的指针指向下一个节点。

删除一个节点,我们将该节点的上一个节点的next指向该节点的下一个节点即可。

使用数组简单实现单向链表,实现基本的操作。

public class SingleLinkedList {

private Node headNode;

private int size;

public SingleLinkedList() {

headNode = null;

size = 0;

}

/**

* 在指定位置插入节点

*

* @param index 位置

* @param e 对象

*/

public void add(int index, E e) {

if (index < 0 || index > size) {

throw new IllegalArgumentException("Add failed. Illegal index.");

}

if (size == 0) {

headNode = new Node(e);

size++;

return;

} else {

//插入为头结点时

if (index == 0) {

Node currentNode = new Node(e);

currentNode.next = headNode;

headNode = currentNode;

size++;

return;

} else {

Node preNode = headNode;

Node currentNode = headNode;

//通过循环,遍历节点直至获取插入位置的上一个节点preNode,及插入位置的节点currentNode

for (int i = 0; i < index; i++) {

preNode=currentNode;

currentNode = currentNode.next;

}

//插入位置为最后时,插入指针没有可以指向的节点,因此赋值为NULL

if (index == size) {

currentNode = null;

}

Node newNode = new Node(e);

//使插入位置的上一个节点指向当前节点

preNode.next = newNode;

newNode.next = currentNode;

}

size++;

}

}

public void addFirst(E e) {

add(0, e);

}

public void addLast(E e) {

add(size,e);

}

public E get(int index) {

if (index < 0 || index >= size) {

throw new IllegalArgumentException("Get failed. Illegal index.");

}

if (index==0){

return headNode.e;

}else {

Node currentNode = headNode;

for (int i = 0; i < index; i++) {

currentNode = currentNode.next;

}

return currentNode.e;

}

}

public E getFirst() {

return get(0);

}

public E getLast() {

return get(size - 1);

}

public void set(int index, E e) {

if (index < 0 || index >= size) {

throw new IllegalArgumentException("set failed. Index is illegal.");

}

Node currentNode = headNode;

for (int i = 0; i < index; i++) {

currentNode = currentNode.next;

}

currentNode.e = e;

}

public void remove(int index) {

if (index < 0 || index >= size) {

throw new IllegalArgumentException("Remove failed. Index is illegal.");

}

if (size == 0) {

size--;

headNode = null;

return;

}

//移除的为头指针时,使头指针的下一个节点

if (index == 0) {

headNode = headNode.next;

size--;

} else {

Node preNode = headNode;

for (int i = 0; i < index-1; i++) {

preNode = headNode.next;

}

//使删除节点的上一个节点指向删除节点的下一个节点

preNode = preNode.next.next;

size--;

}

/**

*

* 扩展

* 1.查询是否包含某个元素

* 2.根据元素的值移除某个元素

* 3.判断链表是否为空

* 4.返回链表的长度

* 5.遍历查询

* ......

*/

}

class Node {

public E e;

public Node next;

public Node(E e) {

this.e = e;

}

}

}

1.2 双端链表

双端链表主要是为了解决在链表末端插入数据的问题。

我们知道单向链表需要在末端插入节点,往往需要遍历获取末端节点,然后指向新节点,因此,考略到这种情况,我们可以使用双端链表:包含一个头结点和一个尾节点,它的简单使用我么可以放在队列中学习。

public class DoublePointLinkedList {

private Node headNode;

private Node tailNode;

private int size;

public DoublePointLinkedList() {

headNode=null;

tailNode=null;

size=0;

}

/**

* 根据索引插入数据可以参考单向链表

*/

public void addHead(E e){

Node currentNode=new Node(e);

if (size==0){

headNode=currentNode;

tailNode=currentNode;

}else{

currentNode.next=headNode;

headNode=currentNode;

}

size++;

}

public void deleteHead(){

if (size==0){

throw new ArrayIndexOutOfBoundsException("Index is illegal");

}

if (headNode.next==null){

headNode=null;

tailNode=null;

}else {

headNode=headNode.next;

}

size--;

}

public void addTail(E e){

Node currentNode=new Node(e);

if (size==0){

headNode=currentNode;

tailNode=currentNode;

}else{

tailNode.next=currentNode;

tailNode=currentNode;

}

size++;

}

public void deleteTail(){

if (size==0){

throw new ArrayIndexOutOfBoundsException("Index is illegal");

}

if (headNode.next==null){

headNode=null;

tailNode=null;

}else {

Node currentNode=headNode;

while (currentNode.next!=tailNode){

currentNode=currentNode.next;

}

currentNode.next=null;

tailNode=currentNode;

}

}

}

1.3 双向链表

双向链表如下图所示,我们知道链表插入速度快,在单向链表中我们若需要查询后面的数据时往往需要从第一个遍历至最后一个,因此我们可以使用双向链表,一个节点指向下个节点,一个节点指向上一个节点。

e041f289d481

双向链表

public class TwoWayLinkedList {

private int size;

private Node head;

private Node tail;

public TwoWayLinkedList() {

size=0;

head=null;

tail=null;

}

public void addHead(E e){

Node node=new Node(e);

if (size==0){

head=node;

tail=node;

}else {

head.prev=node;

node.next=head;

head=node;

}

size++;

}

public void addTail(E e){

Node node=new Node(e);

if (size==0){

head=node;

tail=node;

}else {

node.prev=tail;

tail.next=node;

tail=node;

}

size++;

}

public void deleteHead(){

if (size>0){

head=head.next;

head.prev=null;

size--;

}

}

public void deleteTail(){

if (size>0){

tail=tail.prev;

tail.next=null;

size--;

}

}

class Node {

public E e;

public Node next;

public Node prev;

public Node(E e) {

this.e = e;

}

}

}

当然常用的链表不止这些,还有有序列表等,在以后的算法题中解决!

/* * 基于链表实现树结构 */ package dsa; public class TreeLinkedList implements Tree { private Object element;//树根节点 private TreeLinkedList parent, firstChild, nextSibling;//父亲、长子及最大的弟弟 //(单节点树)构造方法 public TreeLinkedList() { this(null, null, null, null); } //构造方法 public TreeLinkedList(Object e, TreeLinkedList p, TreeLinkedList c, TreeLinkedList s) { element = e; parent = p; firstChild = c; nextSibling = s; } /*---------- Tree接口中各方法的实现 ----------*/ //返回当前节点中存放的对象 public Object getElem() { return element; } //将对象obj存入当前节点,并返回此前的内容 public Object setElem(Object obj) { Object bak = element; element = obj; return bak; } //返回当前节点的父节点;对于根节点,返回null public TreeLinkedList getParent() { return parent; } //返回当前节点的长子;若没有孩子,则返回null public TreeLinkedList getFirstChild() { return firstChild; } //返回当前节点的最大弟弟;若没有弟弟,则返回null public TreeLinkedList getNextSibling() { return nextSibling; } //返回当前节点后代元素的数目,即以当前节点为根的子树的规模 public int getSize() { int size = 1;//当前节点也是自己的后代 TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 size += subtree.getSize();//累加 subtree = subtree.getNextSibling();//所有孩子的后代数目 } return size;//即可得到当前节点的后代总数 } //返回当前节点的高度 public int getHeight() { int height = -1; TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 height = Math.max(height, subtree.getHeight());//在所有孩子中取最大高度 subtree = subtree.getNextSibling(); } return height+1;//即可得到当前节点的高度 } //返回当前节点的深度 public int getDepth() { int depth = 0; TreeLinkedList p = parent;//从父亲开始 while (null != p) {//依次 depth++; p = p.getParent();//访问各个真祖先 } return depth;//真祖先的数目,即为当前节点的深度 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值