数据结构实现
数据结构——数组以及链表的原理这里就不再赘述。
动态数组
package com.nylonmin.dataStructure;
import java.util.Arrays;
/**
* 自行动手实现动态数组
* 需求:
* 当数组元素个数达到底层静态数组的容量上限时,扩容为原来的 2 倍;
* 当数组元素个数缩减到底层静态数组的容量的 1/4 时,缩容为原来的 1/2。
* 实现增删改查功能
* @author nylonmin
*/
public class MyArrayList<T> {
//真正存储数据的底层数组
private T[] data;
//记录当前元素的个数 同样也是从1开始算
private int size;
//默认初始容量
private static final int INIT_CAP=1;
public MyArrayList(){
this(INIT_CAP);
}
public MyArrayList(int initCap){
this.data = (T[])new Object[initCap];
this.size = 0;
}
//增加元素 默认增加到最后
public void append(T num){
//获取总容量
int cap = this.data.length;
if(size == cap){
// 扩容 变为原来的2倍
resize(2*cap);
}
data[size] = num;
size++;
}
//增加元素 到指定的下标
public void append(T num, int index){
checkPositionIndex(index);
int cap=this.data.length;
//判断是否需要扩容
if(size==cap){
resize(cap*2);
}
//转移数据
for(int i=size;i>index;i--){
this.data[i]=this.data[i-1];
}
this.data[index]=num;
size++;
}
//删除指定下标的元素 并返回被删除元素的值
public T deleteAt(int index){
//检查对应下标下是否有元素
checkTrueIndex(index);
int cap = this.data.length;
if(size<cap/4){
resize(cap/2);
}
T result = this.data[index];
for(int i=index;i<size;i++){
this.data[i]=this.data[i+1];
}
this.data[size-1]=null;
size--;
return result;
}
// 查询
public T get(int index){
checkTrueIndex(index);
return this.data[index];
}
//修改
public void set(T num,int index){
checkTrueIndex(index);
this.data[index]=num;
}
//获取当前长度
public int size(){
return this.data.length;
}
//判断是否为空
public boolean isEmpty(){
return this.size==0;
}
//扩容
private void resize(int newCap){
T[] temp =(T[]) new Object[newCap];
for(int i=0;i<this.size;i++){
temp[i] =data[i];
}
data = temp;
}
//判断是否是正常可触及到的index 一般用于删和差
private boolean isLegalTrueIndex(int index){
return index>=0 && index<size;
}
private void checkTrueIndex(int index){
if(!isLegalTrueIndex(index)){
throw new IndexOutOfBoundsException("该下标"+index+"上无元素");
}
}
//判断下标是否合法 或者说判断下标是否是否能够支持插入数据
private boolean isLegalAddIndex(int index){
if(index >=0 && index <= size){
return true;
}
return false;
}
//判断index位置是否可以添加元素
private void checkPositionIndex (int index){
if(!isLegalAddIndex(index)){
throw new IndexOutOfBoundsException(index+"位置的下标无法添加元素");
}
}
@Override
public String toString() {
return "MyArrayList{" +
"data=" + Arrays.toString(data);
}
}
以上代码写的时候有几个有意思的点,用于个人记录:
- 这里面用到了泛型,那么比如说在构造函数中初始化的时候,一个是要通过new Object[] 来初始化,第二个是要强制类型转换.
this.data = (T[])new Object[initCap]; - 在判断下标是否合法/合理的时候,可以通过抛出异常来进行,然后在调用的时候,可以尝试通过try-catch来捕获异常。
双链表实现
实现双链表,不同于单链表,这里本质上有点借鉴了Java中LinkedList的源码实现方法
LinkedList的实现方法实际上就是;
-
使用泛型
-
将class Node作为静态内部类使用
- 每个Node节点都有两个指针,分别指向前一个节点和后一个节点
-
LinkedList本身有两个节点,分别为head和tail ,均为Node
在个人实现中,大体实现了部分功能,用于自我学习。同时在具体实现过程中,head和tail均为虚拟的,只代表头尾节点,并不实际存储任何元素。
package com.nylonmin.LinkedListDemo;
/**
* 自己动手完成双链表数据结构的编写
* 1、定义虚拟头尾节点
* 2、Node类作为静态内部类放在MyLinkedList类中
*/
@SuppressWarnings("all")
public class MyLinkedList<T> {
//定义成员变量
//虚拟头结点
final private Node<T> head;
//虚拟尾节点
final private Node<T> tail;
//当前双链表中实际元素数量
private int size;
/**
* 将Node作为静态内部类放入
* @param <T>
*/
private static class Node<T>{
private Node<T> next;
private Node<T> prev;
private T val;
Node(T val){
this.val=val;
}
@Override
public String toString() {
return "Node{" +
", val=" + val +
'}';
}
}
/**
* 外部类无参构造器
*/
public MyLinkedList(){
//初始化头尾节点
this.head=new Node<>(null);
this.tail=new Node<>(null);
head.next=tail;
tail.prev=head;
this.size=0;
}
/**
* 增s
*/
//插入到头部
public boolean addAtFirst(T val){
Node<T> inserNode = new Node<>(val);
Node<T> node = head.next;
inserNode.next=node;
inserNode.prev=head;
head.next=inserNode;
node.prev=inserNode;
size++;
return true;
}
//插入到尾部
public boolean addAtLast(T val){
Node<T> inserNode = new Node<>(val);
Node<T> node = tail.prev;
inserNode.next=tail;
inserNode.prev=node;
node.next=inserNode;
tail.prev=inserNode;
size++;
return true;
}
//增加到指定下标位置
public boolean add(int index,T val){
//如果index不合规 返回false
if(!isPositionIndex(index)){
return false;
}
if(index ==0 ){
return addAtFirst(val);
}
if(index == size){
return addAtLast(val);
}
Node<T> inserNode = new Node<>(val);
Node<T> node = getNode(index);
Node<T> prevNode = node.prev;
inserNode.next=node;
inserNode.prev=prevNode;
prevNode.next=inserNode;
node.prev=inserNode;
this.size++;
return true;
}
/**
* 删除
*/
public Node<T> remove(int index){
if(!isElementIndex(index)){
return null;
}
Node<T> node = getNode(index);
Node<T> preNode = node.prev;
Node<T> nextNode = node.next;
node.prev=null;
node.next=null;
preNode.next=nextNode;
nextNode.prev=preNode;
return node;
}
/**
* 改
*/
public Node<T> set(int index,T val){
if(!isElementIndex(index)){
return null;
}
Node<T> node = getNode(index);
node.val=val;
return node;
}
/**
* 查
*/
//查询任意index位置节点
public Node<T> get(int index){
return getNode(index);
}
//查询第一个位置节点
public Node<T> getFirst(){
return get(0);
}
//查询最后位置节点
public Node<T> getLast(){
return get(this.size-1);
}
/**
* 工具方法,判断下标是否合规
* @param index
* @return
*/
private boolean isPositionIndex(int index){
if(index<0||index>this.size){
System.out.println(index+"位置无效");
return false;
}
return true;
}
/**
* 工具方法,判断下标是否是实实在在有元素的
* @param index
* @return
*/
private boolean isElementIndex(int index){
if(this.size==0||index<0||index>=this.size){
System.out.println(index+"位置无元素");
return false;
}
return true;
}
/**
* 判断链表是否为空
* @return
*/
public boolean isEmpty(){
return this.size==0;
}
/**
* 获取下标对应节点
* @param index
* @return 返回下标对应的节点
*/
private Node<T> getNode(int index){
//如果下标根本没有元素 那么返回null
if(!isElementIndex(index)){
return null;
}
//由于是双链表 判断从head取还是从tail取好
//如果下标过了一半了 那么从尾部取
if(index >= this.size>>1){
return getNodeFromTail(index);
}
return getNodeFromHead(index);
}
/**
* 为getNode服务,从head头结点取到对应下标的Node
* @param index
* @return
*/
private Node<T> getNodeFromHead(int index){
Node<T> curNode = head;
int curIndex=-1;
while(curIndex!=index){
curNode=curNode.next;
curIndex++;
}
return curNode;
}
/**
* 为getNode服务,从tail尾结点取到对应下标的Node
* @param index
* @return
*/
private Node<T> getNodeFromTail(int index){
Node<T> curNode = tail;
int curIndex=size;
while(curIndex!=index){
curNode=curNode.prev;
curIndex--;
}
return curNode;
}
/**
*
*/
public void printMyList(){
if(this.size==0){
System.out.println("当前链表为空");
return;
}
Node<T> curNode = head.next;
while(curNode.next!=tail){
System.out.print(curNode.val+"->");
curNode=curNode.next;
}
System.out.println(curNode.val);
}
}
测试:
package com.nylonmin.LinkedListDemo;
public class Test {
public static void main(String[] args) {
MyLinkedList<Integer> list = new MyLinkedList<>();
list.addAtLast(1);
list.addAtLast(2);
list.addAtLast(3);
list.addAtFirst(0);
list.add(2, 100);
list.printMyList();
list.remove(0);
list.printMyList();
System.out.println(list.get(3));
// 0->1->100->2->3
//1->100->2->3
//Node{, val=2}
}
}