疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

数据结构Java语言实现之链表


 

 
本系列博文参考书主要为数据结构c语言版(严蔚敏)以及数据结构计算法分析java语言描述,以严蔚敏的数据结构为主要逻辑主线,jdk版本为1.8,代码结构部分参考Java源码,基本贯彻整个数据结构的内容。本节为基础开篇主要是ArrayList与LikedList。
 
ArrayList:
 
java链表的最基本形式之一,除了最基本的增删改查的代码之外,仿照java源码设计了一个迭代器,用于输出控制,这里为了避免与java原有的冲突命名为MyLinkedList,代码如下:
 
package ArrayList;
 
import java.util.Iterator;
 
public class MyArraylist implements Iterable<Object>{
/*
* ArrayList的实现,参考源代码:jdk1.8
* 主要功能,实现ArrayList的基本属性和方法,增加,删除,获取和修改
*/
public static final int DEFAULT_CAPACITY=10;
private int thesize;
private Object [] theItems;
 
public MyArraylist(){
clear();
}
//清空数组
public void clear(){
thesize=0;
ensureCapacity(DEFAULT_CAPACITY);
}
//判断数组是否为空
public boolean isEmpty(){
boolean flage=false;
if(thesize>0){
flage=true;
}
return flage;
}
//返回数组的长度
public int size(){
return thesize;
}
//数组的扩展
public void ensureCapacity(int newCapacity){
if(thesize>newCapacity){
return;
}
Object [] old=theItems;
theItems=new Object[newCapacity];
for(int i=0;i<size();i++){
theItems[i]=old[i];
}
}
 
public boolean add(Object obj){
add(size(),obj);
return true;
}
//增加操作
public void add(int idx,Object obj){
if(thesize>=theItems.length){
ensureCapacity(size()*2+1);
}
for(int i=thesize;i>idx;i--){
theItems[i]=theItems[i--];
}
theItems[idx]=obj;
thesize++;
}
 
//删除操作
public Object remove(int index){
if(index>=size()){
System.out.println("下标越界");
return null;
}
Object removeItem=theItems[index];
for(int i=index;i<size();i++){
theItems[i]=theItems[i+1];
}
thesize--;
return removeItem;
}
//获取数据
public Object get(int index){
if(index>=size()){
System.out.println("下标越界");
return null;
}
Object getItem=theItems[index];
return getItem;
}
//改变数据
public void setItem(int index,Object obj){
if(index>=size()){
throw new IndexOutOfBoundsException();
}
theItems[index]=obj;
}
@Override
public Iterator<Object> iterator() {
// TODO Auto-generated method stub
return new myArrayListIterator();
}
private class myArrayListIterator implements Iterator<Object>{
int currentIndex=0;
public boolean hasNext(){
return (currentIndex<size());
}
public Object next(){
if(!hasNext()){
throw new java.util.NoSuchElementException();
}
return theItems[currentIndex++];
}
public void remove(){
MyArraylist.this.remove(--currentIndex);
}
}
 
}
ArrayList相对较为简单,而java中又封装了指针,所以底层可以看到还是用的数组。在上面的代码中可能看起来比较麻烦的是迭代器和add()方法,迭代器的应用主要是便于输出的控制,而且这里为了自己写的方便所有的方法都是用public关键字,实际上这是不严谨的做法,有些方法应该依据需要来进行控制,例如在中间插入数据的方法,在实际的应用中对于ArrayList都是在末尾插入数据。至于add方法这里面实际上是参考java源码的写法,用了较多嵌套,仔细读一读就会明白,架构跟源码差不多但完整性和合理性就差的远了。
LinkedList,与ArrayList不同之处在于节点是一个类,里面包含三个元素,即上一个节点,下一个节点还有本身自己的数据,相对于java源码下面的实现相对简化了很多,保留了头和尾的两个节点,并且这两个节点不做为数据也不占用实际的数据长度,更重要的是作为一个为位置的标识,代码实现如下:
 
package LinkedList;
 
import java.util.Iterator;
import java.util.NoSuchElementException;
 
public class MyLinkedList {
private int thesize;//链表的实际大小
private Node<Object> header;//链表的头节点,不删除,方便后面的节点的操作
private Node<Object> tailer;//链表的尾节点,不删除。
 
/*
*判断链表是否为空 
*/
private boolean isEmpty(){
return thesize==0;
}
/*
* 清空链表
*/
private void clear() {
header=new Node(null,null,null);
tailer=new Node(null,header,null);
header.next=tailer;
}
public MyLinkedList(){
clear();
};
 
/*
* 返回链表的实际大小,不包括头和尾节点
*/
public int size(){
return thesize;
}
 
/*
*返回值是布尔型 的方法主要是用来做一个判断,看起是否添加成功
*/
public boolean add(Object obj){
add(size(),obj);
return true;
}
 
public void add(int idx,Object obj){
addBefore(getNode(idx),obj);
}
private void addBefore(Node p,Object obj){
Node<Object> newNode=new Node<Object>(obj,p.prev,p);
newNode.prev.next=newNode;
p.prev=newNode;
thesize++;
}
/*
* 删除一个节点
*/
public Object remove(int idx){
return remove(getNode(idx));
}
private Object remove(Node<Object> p){
p.prev.next=p.next;
p.next.prev=p.prev;
thesize--;
return p.data;
}
/*
* 获取某一个节点
*/
private Node<Object> getNode(int idx){
Node<Object> p;
if(idx<0||idx>size()){
throw new IndexOutOfBoundsException();
}
if(idx<size()/2){
p=header.next;
for(int i=0;i<idx;i++){
p=p.next;
}
}else{
p=tailer;
for(int i=size();i>idx;i--){
p=p.prev;
}
}
return p;
}
/*
* 约瑟夫问题:循环删除
*/
public Object solveOfTheJosephus(int startNum,int intervalNum){
//首先对原有的链表做一个改变,时期首尾相连。
tailer.next=header;
header.prev=tailer;
Object end=null;
Node temp=header.next;
Node p=temp;
for(int i=0;i<startNum-1;i++){
p=p.next;
}
while(size()!=1){
for(int i=0;i<intervalNum-1;i++){
p=p.next;
if(p==header||p==tailer){
p=header.next;
}
}
temp=p;
p=p.next;
remove(temp);
}
end=getNode(0).data;
return end;
}
 
/*
* 获取一个迭代器
*/
 
public Iterator<Object> iterator(){
return new LinkedListIterator();
}
private class LinkedListIterator implements Iterator<Object>{
 
private Node<Object> currentNode=header.next;
public boolean hasNext() {
// TODO Auto-generated method stub
return currentNode !=tailer;
}
public Object next() {
// TODO Auto-generated method stub
if(!hasNext()){
throw new NoSuchElementException();
}
Object nextItem=currentNode.data;
currentNode=currentNode.next;
return nextItem;
}
public void remove(){
MyLinkedList.this.remove(currentNode.prev);
}
}
 
 
private class Node<Object>{
public Node(Object d,Node<Object> prev,Node<Object> next){
this.data=d;
this.next=next;
this.prev=prev; 
}
public Node(){}
public Object data;
public Node<Object> prev;
public Node<Object> next;
}
}
这里面的逻辑结构也比较简单跟ArrayList相似,在插入数据的那一块使用了多层的 嵌套,这里面就对一些方法做了范围的控制只留出一些平时可用的方法作为借口,其他的方法全部私有化。这里面我多添加了一个方法,就是约瑟夫问题的解决,约瑟夫问题也被称为循环删除问题,一串数字围成一个圆规定从某一处开始每次数几个数到了哪一个该节点就断开其他的节点在重新链接好重复上面的操作,直到只剩下最后一个数据就将其输出。