目录
01 简介
02 接口设计
03 clear
04 add
05 remove
06 indexOf_toString
07 删除节点
08 反转链表( 递归)
09 反转链表(迭代)
10 反转链表(环形链表)
11 补充
12 虚拟头节点
13 复杂度分析(ArrayList)
14 均摊度分析
15 ArrayList的缩容
16 复杂度震荡
01 简介
- 简介是如下所示:
- 链表的设计方案是如下所示:
- ?链表的设计代码是如下所示:
public class LinkedList {
private int size;
private Node firstNode;//由于是只是在链表之中进行使用,因此我们设计成内部类
private static class Node<E>//这里的节点内部类要进行设计成为相应的私有静态的,不要问为什么.
{
E element;//内部类是一般不写访问的权限的
Node<E> next;
//构造函数
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
02 接口设计
- 链表的接口大部分和动态数组是一致的.因此,创建一个父类List,只是数组和链表的实现方式是不一样的.
? ????????但是这里二者数组和链表内容是完全不一样的,使用父类的方式并不是最佳的方式进行,可以使用接口进行创建.
- ?ArrayList和LinkedList还是存在一定的公共的相同的东西,因此,这里还是可以使用公共的线型表进行表示.
- ?经过上述处理之后List接口的代码是:
/*
*/
public interface List<E> {
static final int ELEMENT_NOT_FOUND = -1;
void clear();
int size();
boolean isEmpty();
boolean contains(E var1);
void add(E var1);
E get(int var1);
E set(int var1, E var2);
void add(int var1, E var2);
E remove(int var1);
int indexOf(E var1);
}
AbstractList之中的代码是: public abstract class AbstractList<E> implements List<E>{//这里是只是实现一些公共的方法,因此,这里是使用abstract方式,对于外界是不可见的.
protected int size;
public int size() {
return size;
}
/**
* 是否为空
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某个元素
* @param element
* @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 添加元素到尾部
* @param element
*/
public void add(E element) {
if(element == null) return;
add(size, element);
}
protected void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
protected void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
protected void rangeCheckForAdd(int index) {
if (index < 0 || index > size) {
outOfBounds(index);
}
}
}
ArrayList之中的代码是:
public class ArrayList<E> extends AbstractList<E> {
/**
* 元素的数量
*/
/**
* 所有的元素
*/
private E[] elements;
private static final int DEFAULT_CAPACITY = 10;
public ArrayList(int capaticy) {
capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy;
elements = (E[]) new Object[capaticy];
}
public ArrayList() {
this(DEFAULT_CAPACITY);
}
/**
* 清除所有元素
*/
public void clear() {
/*
这样的含义就是数组之中都是null,但是数组内存是存在的,不能够进行销毁,
不能够使用element=null,相当于第一根线断了,不能够继续使用.
能循环利用的留下,不能够循环利用的滚蛋.
*/
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
}
/**
* 获取index位置的元素
* @param index
* @return
*/
public E get(int index) {
rangeCheck(index);
return elements[index];
}
/**
* 设置index位置的元素
* @param index
* @param element
* @return 原来的元素?
*/
public E set(int index, E element) {
rangeCheck(index);
E old = elements[index];
elements[index] = element;
return old;
}
/**
* 在index位置插入一个元素
* @param index
* @param element
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacity(size + 1);
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
/**
* 删除index位置的元素
* @param index
* @return
*/
public E remove(int index) {
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i < size; i++) {
elements[i - 1] = elements[i];
}
elements[--size] = null;
return old;
}
public void remove(E element)
{
remove(indexOf(element));
}
/**
* 查看元素的索引
* @param element
* @return
*/
public int indexOf(E element) {
if (element == null) { // 1
for (int i = 0; i < size; i++) {
if (elements[i] == null) return i;
}
} else {
for (int i = 0; i < size; i++) {
if (element.equals(elements[i])) return i; // n
}
}
return ELEMENT_NOT_FOUND;
}
// public int indexOf2(E element) {
// for (int i = 0; i < size; i++) {
// if (valEquals(element, elements[i])) return i; // 2n
// }
// return ELEMENT_NOT_FOUND;
// }
//
// private boolean valEquals(Object v1, Object v2) {
// return v1 == null ? v2 == null : v1.equals(v2);
// }
/**
* 保证要有capacity的容量
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println(oldCapacity + "扩容为" + newCapacity);
}
@Override
public String toString() {
// size=3, [99, 88, 77]
StringBuilder string = new StringBuilder();
string.append("size=").append(size).append(", [");
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(", ");
}
string.append(elements[i]);
// if (i != size - 1) {
// string.append(", ");
// }
}
string.append("]");
return string.toString();
}
}
LinkedList之中的代码是:
public class LinkedList<E> extends AbstractList<E>{
int ELEMENT_NOT_FOUND = -1;
private int size;
private Node firstNode;//由于是只是在链表之中进行使用,因此我们设计成内部类
private static class Node<E>//这里的节点内部类要进行设计成为相应的私有静态的,不要问为什么.
{
E element;//内部类是一般不写访问的权限的
Node<E> next;
//构造函数
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
public void clear() {
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND ;
}
public void add(E element) {
add(size,element);
}
public E get(int var1) {
return null;
}
public E set(int var1, E var2) {
return null;
}
public void add(int var1, E var2) {
}
public E remove(int var1) {
return null;
}
public int indexOf(E var1) {
return 0;
}
}
03 clear
- 清空元素
- ?代码块如下所示:
public void clear() {
size = 0;
first = null;
}
04 add
- 添加元素的结构示意图:
- ?通过index获取相应的节点的方式是如下所示:
/*
获取index对应位置的节点对象
*/
private Node<E> node(int index)//这里的含义就是传入一个索引,返回当下的节点
{
//检查索引是否存在
rangeCheck(index);
//比如,想要找到索引为2的位置,需要索引进行next两次
Node<E> node = first;
for (int i = 0;i < index;i++)
{
node = node.next;
}
return node;
}
- 由上所示,可以得知set和get方法的使用:
public E get(int index) {
return node(index).element;
}
public E set(int index, E element) {
Node<E> node = node(index);
E old = node.element;
node.element = element;
return old;
}
- 添加元素的代码是如下所示:
public void add(int index, E element) {
//如果要是传入的index是0
if(index == 0)//添加到最前面的位置
{
first = new Node<>(element,first);
}
else
{ Node<E> prev = node(index-1);
prev.next = new Node<E>(element,prev.next); //这是
}
size++;
} ?注意:临界和边界位置处理
05 remove
- 删除元素
public E remove(int index) {
//被删除的节点
Node<E> node = first;
if(index == 0)
{
first = first.next;
}
else
{
Node<E> prev = node(index-1);
node = prev.next;
prev.next = prev.next.next;
}
size--;
return node.element;
}
06 indexOf_toString
private Node<E> node(int index)//这里的含义就是传入一个索引,返回当下的节点
{
//检查索引是否存在
rangeCheck(index);
//比如,想要找到索引为2的位置,需要索引进行next两次
Node<E> node = first;
for (int i = 0;i < index;i++)
{
node = node.next;
}
return node;
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size=").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(", ");
}
string.append(node.element);
node = node.next;
}
string.append("]");
return string.toString();
}
07 删除节点
08 反转链表( 递归)
刷题:
-
-
代码段如下所示: package 链表;
public class _206_反转链表 {
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public ListNode reverseList(ListNode head) {
if(head == null)
return null;
if(head.next == null) return head;
ListNode newHead = reverseList(head.next);
head.next.next = head;//4的next指向5
head.next = null;//5的next指向null
return newHead;
}
}
} -
09 反转链表(迭代)
10 反转链表(环形链表)
11 补充
- 这里需要进行注意的是remove方法之中存在首指针为0的情况,add方法之中也是需要进行注意:
12 虚拟头节点
- 使得代码更加的精简
- ?虚拟头节点的存在使得相应的add和remove以及toString方式存在差异.
- ArrayList2代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.mj;
public class ArrayList2<E> extends AbstractList<E> {
private E[] elements;
private static final int DEFAULT_CAPACITY = 10;
public ArrayList2(int capaticy) {
capaticy = capaticy < 10 ? 10 : capaticy;
this.elements = new Object[capaticy];
}
public ArrayList2() {
this(10);
}
public void clear() {
for(int i = 0; i < this.size; ++i) {
this.elements[i] = null;
}
this.size = 0;
if (this.elements != null && this.elements.length > 10) {
this.elements = new Object[10];
}
}
public E get(int index) {
this.rangeCheck(index);
return this.elements[index];
}
public E set(int index, E element) {
this.rangeCheck(index);
E old = this.elements[index];
this.elements[index] = element;
return old;
}
public void add(int index, E element) {
this.rangeCheckForAdd(index);
this.ensureCapacity(this.size + 1);
for(int i = this.size; i > index; --i) {
this.elements[i] = this.elements[i - 1];
}
this.elements[index] = element;
++this.size;
}
public E remove(int index) {
this.rangeCheck(index);
E old = this.elements[index];
for(int i = index + 1; i < this.size; ++i) {
this.elements[i - 1] = this.elements[i];
}
this.elements[--this.size] = null;
this.trim();
return old;
}
public int indexOf(E element) {
int i;
if (element == null) {
for(i = 0; i < this.size; ++i) {
if (this.elements[i] == null) {
return i;
}
}
} else {
for(i = 0; i < this.size; ++i) {
if (element.equals(this.elements[i])) {
return i;
}
}
}
return -1;
}
private void ensureCapacity(int capacity) {
int oldCapacity = this.elements.length;
if (oldCapacity < capacity) {
int newCapacity = oldCapacity + (oldCapacity >> 1);
Object[] newElements = new Object[newCapacity];
for(int i = 0; i < this.size; ++i) {
newElements[i] = this.elements[i];
}
this.elements = newElements;
System.out.println(oldCapacity + "扩容为" + newCapacity);
}
}
private void trim() {
int oldCapacity = this.elements.length;
int newCapacity = oldCapacity >> 1;
if (this.size <= newCapacity && oldCapacity > 10) {
Object[] newElements = new Object[newCapacity];
for(int i = 0; i < this.size; ++i) {
newElements[i] = this.elements[i];
}
this.elements = newElements;
System.out.println(oldCapacity + "缩容为" + newCapacity);
}
}
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size=").append(this.size).append(", [");
for(int i = 0; i < this.size; ++i) {
if (i != 0) {
string.append(", ");
}
string.append(this.elements[i]);
}
string.append("]");
return string.toString();
}
}
一般不建议是使用添加虚拟头节点的方式进行.
13 复杂度分析(ArrayList)
14 均摊度分析
- ?适用情况:经过连续的多次复杂度比较低的情况后,出现个别复杂度比较高的情况
15 ArrayList的缩容
- 当动态数组存在较多的容量的时候,就是需要进行一个缩容的操作.一般是存在相应的删除的过程.
private void trim() {
int oldCapacity = this.elements.length;
int newCapacity = oldCapacity >> 1;//首先是将原始的容量变成原始的一半,
if (this.size <= newCapacity && oldCapacity > 10) { //如果剩余的空间是原来的容量的一半,
Object[] newElements = new Object[newCapacity];//声明一个新的容量与数组
for(int i = 0; i < this.size; ++i) {
newElements[i] = this.elements[i];
}
this.elements = newElements;
System.out.println(oldCapacity + "缩容为" + newCapacity);
}
}
16 复杂度震荡
- 如果要是扩容倍数和缩容时机设计不得当,会导致复杂度震荡.
|