玩转数据结构
1、为什么要学习数据结构?
-
数据结构是所有计算机专业的同学必学的课程
-
数据结构研究的是数据如何在计算机中进行组织和存储,使我们可以高效的获取数据或者修改数据。
1.1、数据结构分为3种结构
1.1.1、线性结构
数组;栈;队列;链表;哈希表
1.1.2、树结构
二叉树;二分搜索树;AVL;红黑树;Treep;Splay;堆;Trie;线段树;K-D树;并查集;哈夫曼树;
1.1.3、图结构
邻接矩阵;邻接表
2、不要小瞧数组
2.1、数组基础
-
把数据码成一排进行存放
scores 0 1 2 3 4 5 6 7 .... 索引
-
数组最大的优点: 快速查询。 scores[2]
-
数组最好应用于“索引有语意” 的情况。
-
但并非所有有语意的索引都适用于数组
身份证号
-
数组也可以处理“索引没有语意” 的情况。
2.2、制作自己的数组类
/**
* 数组
*/
public class Array {
private int [] data; // 元素中数据
private int size; // 元素个数
/**
* 构造函数,传入数组的容量capacity 构造Array
* @param capacity
*/
public Array(int capacity){
data = new int[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity = 10
public Array(){
this(10);
}
/**
* 获取数组中的元素个数
* @return
*/
public int getSize(){
return size;
}
/**
* 获取数组的容量
* @return
*/
public int getCapacity(){
return data.length;
}
/**
* 数组是否为null
* @return
*/
public boolean isEmpty(){
return size == 0;
}
}
2.3、向数组中添加元素
- 向数组末尾添加元素
/**
* 向指定位置添加元素
* @param index 索引
* @param e 新元素
*/
public void add(int index,int e){
// 数组个数和数组相等,抛出异常
if(size == data.length)
throw new IllegalArgumentException("Add failed.Array is full");
if(index < 0 || index > size){
throw new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
}
for(int i = size-1; i >=index;i-- ){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
2.4、数组中查询元素和修改元素
/**
* 获取index 索引位置的元素
* @param index
* @return
*/
int get(int index){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}
/**
* 设置索引的值
* @param index
* @param e
* @return
*/
void set(int index,int e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index] = e;
}
2.5、包含,搜索和删除
2.5.1、包含
/**
* 查找数据中是否有元素E
* @param e
* @return
*/
public boolean contains(int e){
for(int i = 0;i < size; i++ ){
if(data[i] == e){
return true;
}
}
return false;
}
2.5.2、搜索
/**
* 查找数组中e所在的索引,如果不存在元素E,则返回-1
* @param e
* @return
*/
public int find(int e){
for(int i = 0;i <size;i++ ){
if(data[i] == e){
return i;
}
}
return -1;
}
2.5.3、删除
/**
* 删除索引中的元素,返回删除的元素
* @param index
* @return
*/
public int remove(int index){
if (index < 0 || index > size ){
throw new IllegalArgumentException("remove failed,Index is illegal");
}
int ret = data[index];
for(int i = index +1; i < size; i++ ){
data[i-1] = data[i]; //数组中 之后元素的值向前挪动
}
size--;
return ret;
}
/**
* 删除第一个元素
* @return
*/
public int removeFirst(){
return remove(0);
}
/**
* 删除末尾元素
* @return
*/
public int removeLast(){
return remove(size-1);
}
/**
* 从数组中删除 元素E
* @param e
*/
public void removeElement(int e){
int index = find(e);
if (index != -1){
remove(index);
}
}
数组工具 完整例子:
package com.zf;
/**
* 数组
*/
public class Array {
private int [] data;
private int size;
/**
* 构造函数,传入数组的容量capacity 构造Array
* @param capacity
*/
public Array(int capacity){
data = new int[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity = 10
public Array(){
this(10);
}
/**
* 获取数组中的元素个数
* @return
*/
public int getSize(){
return size;
}
/**
* 获取数组的容量
* @return
*/
public int getCapacity(){
return data.length;
}
/**
* 数组是否为null
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 数组末尾 添加一个新元素
* @param e
*/
public void addLast(int e){
// // 数组个数和数组相等,抛出异常
// if(size == data.length)
// throw new IllegalArgumentException("Addlast failed.Array is full");
// data[size] = e;
// size++; // data[size++] = e;
add(size,e);
}
/**
* 数组开头 添加一个新的元素
* @param e
*/
public void addFirst(int e){
add(0,e);
}
/**
* 向指定位置添加元素
* @param index 索引
* @param e 新元素
*/
public void add(int index,int e){
// 数组个数和数组相等,抛出异常
if(size == data.length)
throw new IllegalArgumentException("Add failed.Array is full");
if(index < 0 || index > size){
throw new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
}
for(int i = size-1; i >=index;i-- ){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
/**
* 获取index 索引位置的元素
* @param index
* @return
*/
public int get(int index){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}
/**
* 设置索引的值
* @param index
* @param e
* @return
*/
public void set(int index,int e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index] = e;
}
/**
* 查找数据中是否有元素E
* @param e
* @return
*/
public boolean contains(int e){
for(int i = 0;i < size; i++ ){
if(data[i] == e){
return true;
}
}
return false;
}
/**
* 查找数组中e所在的索引,如果不存在元素E,则返回-1
* @param e
* @return
*/
public int find(int e){
for(int i = 0;i <size;i++ ){
if(data[i] == e){
return i;
}
}
return -1;
}
/**
* 删除索引中的元素,返回删除的元素
* @param index
* @return
*/
public int remove(int index){
if (index < 0 || index > size ){
throw new IllegalArgumentException("remove failed,Index is illegal");
}
int ret = data[index];
for(int i = index +1; i < size; i++ ){
data[i-1] = data[i]; //数组中 之后元素的值向前挪动
}
size--;
return ret;
}
/**
* 删除第一个元素
* @return
*/
public int removeFirst(){
return remove(0);
}
/**
* 删除末尾元素
* @return
*/
public int removeLast(){
return remove(size-1);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
sb.append("[");
for(int i = 0;i < size; i++ ) {
sb.append(data[i]);
if(i != size - 1){
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
2.6、使用泛型
- 让我们的数据结构可以放置“任何”数据类型
- 不可以是基本数据烈行,只能是类对象
boolean,byte,char,short,int,long,float,double
- 每个基本数据类型都有对应的包装类
Boolean,Byte,Char,Short,Int,Long,Float,Double
package com.zf;
/**
* 数组
*/
public class ArrayListDemo<E> {
private E[] data;
private int size;
/**
* 构造函数,传入数组的容量capacity 构造Array
* @param capacity
*/
public ArrayListDemo(int capacity){
data = (E[])new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity = 10
public ArrayListDemo(){
this(10);
}
/**
* 获取数组中的元素个数
* @return
*/
public int getSize(){
return size;
}
/**
* 获取数组的容量
* @return
*/
public int getCapacity(){
return data.length;
}
/**
* 数组是否为null
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 数组末尾 添加一个新元素
* @param e
*/
public void addLast(E e){
// // 数组个数和数组相等,抛出异常
// if(size == data.length)
// throw new IllegalArgumentException("Addlast failed.Array is full");
// data[size] = e;
// size++; // data[size++] = e;
add(size,e);
}
/**
* 数组开头 添加一个新的元素
* @param e
*/
public void addFirst(E e){
add(0,e);
}
/**
* 向指定位置添加元素
* @param index 索引
* @param e 新元素
*/
public void add(int index,E e){
// 数组个数和数组相等,抛出异常
if(size == data.length)
throw new IllegalArgumentException("Add failed.Array is full");
if(index < 0 || index > size){
throw new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
}
for(int i = size-1; i >=index;i-- ){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
/**
* 获取index 索引位置的元素
* @param index
* @return
*/
public E get(int index){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}
/**
* 设置索引的值
* @param index
* @param e
* @return
*/
public void set(int index,E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index] = e;
}
/**
* 查找数据中是否有元素E
* @param e
* @return
*/
public boolean contains(E e){
for(int i = 0;i < size; i++ ){
if(data[i].equals(e)){
return true;
}
}
return false;
}
/**
* 查找数组中e所在的索引,如果不存在元素E,则返回-1
* @param e
* @return
*/
public int find(E e){
for(int i = 0;i <size;i++ ){
if(data[i].equals(e)){
return i;
}
}
return -1;
}
/**
* 删除索引中的元素,返回删除的元素
* @param index
* @return
*/
public E remove(int index){
if (index < 0 || index > size ){
throw new IllegalArgumentException("remove failed,Index is illegal");
}
E ret = data[index];
for(int i = index +1; i < size; i++ ){
data[i-1] = data[i]; //数组中 之后元素的值向前挪动
}
size--;
// data[size] = null;
return ret;
}
/**
* 删除第一个元素
* @return
*/
public E removeFirst(){
return remove(0);
}
/**
* 删除末尾元素
* @return
*/
public E removeLast(){
return remove(size-1);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
sb.append("[");
for(int i = 0;i < size; i++ ) {
sb.append(data[i]);
if(i != size - 1){
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
2.7、动态数组
- 扩容空间
/**
* 扩容空间
* @param newCapacity
*/
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
public void add(int index,E e){
// 数组个数和数组相等,抛出异常
if(index < 0 || index > size){
throw new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
}
if(size == data.length)
resize(2 * data.length); //扩容数组
for(int i = size-1; i >=index;i-- ){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
/**
* 删除索引中的元素,返回删除的元素
* @param index
* @return
*/
public E remove(int index){
if (index < 0 || index > size ){
throw new IllegalArgumentException("remove failed,Index is illegal");
}
E ret = data[index];
for(int i = index +1; i < size; i++ ){
data[i-1] = data[i]; //数组中 之后元素的值向前挪动
}
size--;
// data[size] = null;
//如果当前数组容量 等于数组一半
if(size == data.length / 4 && data.length / 2 != 0 )
resize(data.length / 2); // 缩容数组 ,容量缩小
return ret;
}
ArratListDemo
package com.zf;
/**
* 数组
*/
public class ArrayListDemo<E> {
private E[] data;
private int size;
/**
* 构造函数,传入数组的容量capacity 构造Array
* @param capacity
*/
public ArrayListDemo(int capacity){
data = (E[])new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity = 10
public ArrayListDemo(){
this(10);
}
/**
* 获取数组中的元素个数
* @return
*/
public int getSize(){
return size;
}
/**
* 获取数组的容量
* @return
*/
public int getCapacity(){
return data.length;
}
/**
* 数组是否为null
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 数组末尾 添加一个新元素
* @param e
*/
public void addLast(E e){
// // 数组个数和数组相等,抛出异常
// if(size == data.length)
// throw new IllegalArgumentException("Addlast failed.Array is full");
// data[size] = e;
// size++; // data[size++] = e;
add(size,e);
}
/**
* 数组开头 添加一个新的元素
* @param e
*/
public void addFirst(E e){
add(0,e);
}
/**
* 向指定位置添加元素
* @param index 索引
* @param e 新元素
*/
public void add(int index,E e){
// 数组个数和数组相等,抛出异常
if(index < 0 || index > size){
throw new IllegalArgumentException("Add failed.Require index >=0 and index <= size");
}
if(size == data.length)
resize(2 * data.length);
for(int i = size-1; i >=index;i-- ){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
/**
* 获取index 索引位置的元素
* @param index
* @return
*/
public E get(int index){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}
/**
* 取出第一个元素
* @return
*/
public E getFirst(){
return get(0);
}
/**
* 取出最后一个元素
* @return
*/
public E getLast(){
return get(size-1);
}
/**
* 设置索引的值
* @param index
* @param e
* @return
*/
public void set(int index,E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index] = e;
}
/**
* 查找数据中是否有元素E
* @param e
* @return
*/
public boolean contains(E e){
for(int i = 0;i < size; i++ ){
if(data[i].equals(e)){
return true;
}
}
return false;
}
/**
* 查找数组中e所在的索引,如果不存在元素E,则返回-1
* @param e
* @return
*/
public int find(E e){
for(int i = 0;i <size;i++ ){
if(data[i].equals(e)){
return i;
}
}
return -1;
}
/**
* 删除索引中的元素,返回删除的元素
* @param index
* @return
*/
public E remove(int index){
if (index < 0 || index > size ){
throw new IllegalArgumentException("remove failed,Index is illegal");
}
E ret = data[index];
for(int i = index +1; i < size; i++ ){
data[i-1] = data[i]; //数组中 之后元素的值向前挪动
}
size--;
// data[size] = null;
//如果当前数组容量 等于数组一半
if(size == data.length / 2)
resize(data.length / 2); // 缩容数组 ,容量缩小
return ret;
}
/**
* 删除第一个元素
* @return
*/
public E removeFirst(){
return remove(0);
}
/**
* 删除末尾元素
* @return
*/
public E removeLast(){
return remove(size-1);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
sb.append("[");
for(int i = 0;i < size; i++ ) {
sb.append(data[i]);
if(i != size - 1){
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
/**
* 扩容空间
* @param newCapacity
*/
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
}
3、栈 Stack
3.1、栈和栈的应用:撤销操作和操作系统
- 栈也是一种线性结构
- 相比数组,栈对应的操作是数组的子集
- 只能从一端添加元素,也只能从一端取出元素
- 这一端称为栈顶
| 4 栈顶 |
|---|
| 3 |
| 2 |
| 1 |
-
栈是一种后进先出的数据结构 (先进去1,2,3,4 ) 取的时候 先取(4,3,2,1)
-
在计算机的世界中,栈拥有者不可思议的作用
-
撤销操作 (栈理解成撤销)
-
程序调用的系统栈
3.2、栈的基本实现
- 从用户的角度看,支持这些操作就好
- 具体底层实现,用户不关心
- 实际底层有多种实现方式
接口
Interface Stack<E> {
void push(E); //添加元素,入栈
E pop(); // 出栈,拿出栈顶元素
E peek(); // 看栈顶的元素是谁
int getSize(); // 栈一共多少个元素
boolean isEmpty(); // 栈是否为空
}
实现类:
class ArrayStack implement Stack<E>
3.3、栈的另一个应用:括号和匹配
- undo 操作 - 编译器
- 系统调用栈 - 操作系统
- 括号匹配 - 编译器
3.4、关于Leetcode的更多说明
给定一个只包括 () {} [] 的字符串,判断字符串是否有效,括号必须以正确的顺序关闭, () {} [] 是有效的,(】 {] 不是有效的
解决思路:
Solution.java
public class Solution {
/**
* 判断输入的值是否匹配
* @param chatStr ({[]})
* @return
*/
public boolean isValid(String chatStr){
Stack<String> stack = new Stack<>();
for (int i = 0; i < chatStr.length(); i++ ){
String s = String.valueOf(chatStr.charAt(i));
if(s.equals("(") || s.equals("{") || s.equals("[")){
stack.push(s);
}else{
if(stack.isEmpty()){
return false;
}
// 得到入栈中的 栈顶元素 ( { [
String topStr = stack.pop();
// 判断栈顶的元素 跟输入的内容是否匹配,如 () {} []
if(s.equals(")") && !topStr.equals("(")){
return false;
}
if(s.equals("}") && !topStr.equals("{")){
return false;
}
if(s.equals("]") && !topStr.equals("[")){
return false;
}
}
}
return stack.isEmpty();
}
}
3.5、 数组队列 Queue
-
队列也是一种线性结构
-
相比数组,队列对应的操作是数组的子集
-
只能从一端(队尾)添加元素,只能从另一端(对首)取出元素
-
队列是一种先进先出的数据结构(先到先得)
队列的实现
interface Queue<E>
void enqueue(E); // 入队
E dequeue(); // 出队
E getFront(); // 队首
int getSize(); //
boolean isEmpty();
ArrayQueue
package com.zf.com.zf.queue;
import com.zf.ArrayListDemo;
public class ArrayQueue<E> implements Queue<E> {
private ArrayListDemo<E> array;
public ArrayQueue(int capacity) {
array = new ArrayListDemo<>(capacity);
}
public ArrayQueue() {
array = new ArrayListDemo<>();
}
@Override
public void enqueue(E e) {
array.addLast(e);
}
@Override
public E dequeue() {
return array.removeFirst();
}
@Override
public E getFront() {
return array.getFirst();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity(){
return array.getCapacity();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Queue: ");
sb.append("front [");
for(int i = 0 ; i < array.getSize();i ++ ){
sb.append(array.get(i));
if( i != array.getSize() - 1 ){
sb.append(", ");
}
}
sb.append("] tail ");
return sb.toString();
}
}
3.6、循环队列
front == tail 队列为空
tail+1 == front 队列满 (tail+1) % c == front 队列满
数组队列的问题
跟钟表一样
LoopQueue
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front,tail;
private int size;
/**
* 初始构造函数
* @param capacity
*/
public LoopQueue(int capacity) {
data = (E[])new Object[capacity+1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
/**
* 数组长度 -1
* @return
*/
public int getCapacity(){
return data.length - 1;
}
@Override
public void enqueue(E e) {
}
@Override
public E dequeue() {
return null;
}
@Override
public E getFront() {
return null;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
}
3.7、循环队列的实现
LoopQueue.java
package com.zf.com.zf.queue;
import java.util.Random;
/**
* 循环队列
* @param <E>
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data; //队列数据
private int front,tail; // 队首 队尾下一个位置
private int size; //个数
/**
* 初始构造函数
* @param capacity
*/
public LoopQueue(int capacity) {
data = (E[])new Object[capacity+1]; //循环队列 浪费一个单位 容积+1
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
/**
* 循环队列 浪费一个单位 容积+1
* 数组长度 -1
* @return
*/
public int getCapacity(){
return data.length - 1;
}
@Override
public void enqueue(E e) {
//判断队列是否满了
if( (tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail+1)%data.length;
size++;
}
/**
* 扩容数组
* @param newCapacity
*/
private void resize(int newCapacity){
//新的数据
E[] newData = (E[])new Object[newCapacity + 1];
for(int i = 0 ;i < size; i++ ){
//将原来data中的数据放到新的data中
newData[i] = data[(i+front) % data.length];
}
data = newData;
front = 0;
tail = size;
}
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("Cannot dequeue from an empty queue");
}
E ret = data[front];
data[front] = null;
front = (front + 1) %data.length;
size--;
if(size== getCapacity() / 4 && getCapacity() / 2 != 0){
resize(getCapacity()/2);
}
return ret;
}
@Override
public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("Cannot dequeue from an empty queue");
}
return data[front];
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Queue:size = %d ,capacity = %d \n",size,getCapacity()));
sb.append("front [");
for(int i = front; i !=tail; i = ( i + 1 ) % data.length ){
sb.append(data[i]);
if ( ( i + 1 ) % data.length != tail ){
sb.append(", ");
}
}
sb.append("] tail");
return sb.toString();
}
// 测试使用q 运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
private static double testQueue(Queue<Integer> q,int opCount){
long startTime = System.nanoTime();
Random r = new Random();
for (int i =0;i < opCount ; i++ ){
q.enqueue(r.nextInt(Integer.MAX_VALUE));
}
for (int i = 0; i < opCount ; i ++ ){
q.dequeue();
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
int opCount = 100000;
LoopQueue<Integer> loopQueue = new LoopQueue<>();
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
System.out.println("ArrayQueue "+testQueue(arrayQueue,opCount) +"S");
testQueue(loopQueue,opCount);
System.out.println("testQueue "+testQueue(loopQueue,opCount) +"S");
}
}
4、什么是链表?
4.1、什么是链表?
线性数据结构
底层依托静态数组
靠resize解决固定容量问题
- 动态数组
- 栈
- 队列
为什么链表很重要
- 链表 真正的动态数据结构
- 最简单的动态数据结构
- 更深入的理解引用(或者指针)
- 更深入的理解递归
链表LinkedList
- 数据存储在“节点”(Node)中
// 链表跟火车一样 一阶一阶的 next 是null 是最后一个节点
class Node {
E e; // 存储真正的数据
Node next; // 指向当前节点的 下一个节点
}
// 1 → 2 → 3 → null
- 优点:真正的动态,不需要处理固定容量的问题
- 缺点:丧失了随机访问的能力
数据和链表的对比
-
数组最好用于索引有语意的情况。socres[2]
-
最大的优点: 支持快速查询
-
链表不适用于索引有语意的情况
-
最大优点:动态
LinkedList.java
package com.zf.linked;
/**
* 链表
* @param <E>
*/
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e,Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString() {
return e.toString();
}
}
}
4.2、在链表中添加元素
在链表头添加元素
node.next = head
head = node;
size++;
在链表中间添加元素
在索引为2的地方添加元素666
- 关键:找到要添加的节点的前一个节点 顺序很重要
node.next = prev.next;
prev.next = node;
LinkedList.java
package com.zf.linked;
/**
* 链表
* @param <E>
*/
public class LinkedList<E> {
private Node head; // 头部
private int size;
public LinkedList(){
head = null;
size = 0;
}
//获取元素中的元素个数
public int getSize(){
return size;
}
//返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
//在链表头添加新的元素e
public void addFirst(E e){
// Node node = new Node(e);
//// node.next = head; //元素头
//// head = node; //head 更新一下
head = new Node(e,head);
size++;
}
//在链表的index(0-based)位置添加新的元素e
//在链表中不是一个常用的操作,练习用
public void add(int index,E e){
if(index < 0 || index > size ){
throw new IllegalArgumentException("Add failed. Illegal index.");
}
if(index == 0){
addFirst(e);
}else{
Node prev = head; //下一个节点
for( int i = 0; i < index - 1;i++ ){
prev = prev.next; //当前节点 = 下一个节点
}
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
prev.next = new Node(e,prev.next);
size++;
}
}
/**
* 向元素末尾添加一个新的元素e
* @param e
*/
public void addLast(E e){
add(size,e);
}
private class Node {
public E e;
public Node next;
public Node(E e,Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString() {
return e.toString();
}
}
}
4.3、使用链表的虚拟头结点
为链表设立虚拟头节点
null → 0 → 1 → 2 → 3 → 4 → null
↑
dummyHead
package com.zf.linked;
/**
* 链表
* @param <E>
*/
public class LinkedList<E> {
private Node dymmyHead; // 虚拟头节点
private int size;
public LinkedList(){
dymmyHead = new Node(null,null);
size = 0;
}
//获取元素中的元素个数
public int getSize(){
return size;
}
//返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
//在链表头添加新的元素e
public void addFirst(E e){
// Node node = new Node(e);
//// node.next = head; //元素头
//// head = node; //head 更新一下
add(0,e);
size++;
}
//在链表的index(0-based)位置添加新的元素e
//在链表中不是一个常用的操作,练习用
public void add(int index,E e){
if(index < 0 || index > size ){
throw new IllegalArgumentException("Add failed. Illegal index.");
}
Node prev = dymmyHead; //下一个节点
for( int i = 0; i < index;i++ ){
prev = prev.next; //当前节点 = 下一个节点
}
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
prev.next = new Node(e,prev.next);
size++;
}
/**
* 向元素末尾添加一个新的元素e
* @param e
*/
public void addLast(E e){
add(size,e);
}
private class Node {
public E e;
public Node next;
public Node(E e,Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString() {
return e.toString();
}
}
}
4.4、链表的遍历,查询和修改
LinkedList.java
package com.zf.linked;
/**
* 链表
* @param <E>
*/
public class LinkedList<E> {
private Node dymmyHead; // 头部
private int size;
public LinkedList(){
dymmyHead = new Node(null,null);
size = 0;
}
//获取元素中的元素个数
public int getSize(){
return size;
}
//返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
//在链表头添加新的元素e
public void addFirst(E e){
// Node node = new Node(e);
//// node.next = head; //元素头
//// head = node; //head 更新一下
add(0,e);
size++;
}
//在链表的index(0-based)位置添加新的元素e
//在链表中不是一个常用的操作,练习用
public void add(int index,E e){
if(index < 0 || index > size ){
throw new IllegalArgumentException("Add failed. Illegal index.");
}
Node prev = dymmyHead; //前一个
for( int i = 0; i < index;i++ ){
prev = prev.next; //当前节点 = 下一个节点
}
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
prev.next = new Node(e,prev.next);
size++;
}
/**
* 向元素末尾添加一个新的元素e
* @param e
*/
public void addLast(E e){
add(size,e);
}
/**
* 获得链表的第index(0-based)个位置元素
* 在链表中不是一个常用的操作,练习用
* @param index
* @return
*/
public E get(int index){
if(index < 0 || index >= size ){
throw new IllegalArgumentException("Add failed. Illegal index.");
}
Node cur = dymmyHead.next; // 从索引为0 的元素遍历
for(int i = 0;i< index;i ++ ){
cur = cur.next;
}
return cur.e;
}
//获得链表第一个元素
public E getFirst(){
return get(0);
}
// 获得链表最后一个元素
public E getLast(){
return get(size-1);
}
// 修改链表的第index(0-based)个位置的元素为e
// 练习用
public void set(int index,E e){
if(index < 0 || index >= size ){
throw new IllegalArgumentException("update failed. Illegal index.");
}
Node cur = dymmyHead.next;
for(int i = 0;i< index;i ++ ){
cur = cur.next;
}
cur.e = e;
}
//查找链表中是否有元素e
public boolean contains(E e){
Node cur = dymmyHead.next;
while(cur != null ){
if (cur.e.equals(e)){
return true;
}
cur = cur.next;
}
return false;
}
private class Node {
public E e;
public Node next;
public Node(E e,Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString() {
return e.toString();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Node cur = dymmyHead.next;
while(cur != null ){
sb.append(cur + "->");
cur = cur.next;
}
// for 循环
// for(Node cur = dymmyHead.next; cur != null; cur = cur.next){
// sb.append(cur + "->");
// }
// sb.append("NULL");
sb.append("NULL");
return sb.toString();
}
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 5; i++) {
linkedList.addFirst(i);
System.out.println(linkedList);
}
linkedList.add(2,666);
System.out.println(linkedList);
}
}
4.5、从链表中删除元素
链表元素的删除
- 删除索引为2位置的元素
prev.next = delNode.next
delNode.next = null
/**
* 从链表中删除元素
* @param index
* @return
*/
public E remove(int index){
if(index < 0 || index >= size ){
throw new IllegalArgumentException("Remove failed. Illegal index.");
}
Node prev = dymmyHead;
for(int i = 0;i < index; i ++ ){
prev = prev.next;
}
Node retNode = prev.next; // 实例 删除节点
prev.next = retNode.next; // 下个节点 = 删除节点
retNode.next = null; // 删除的节点设置为null
size--;
return retNode.e;
}
4.6、使用链表实现栈
interface Stack<E>
5、链表和递归
5.1、递归基础与递归的宏观语意
递归
-
本质上,将原来的问题,转化为更小的同一问题
-
举例: 数组求和
Sum( arr[0...n-1]) = arr[0] + Sum(arr[1....n-1]) ← 更小的同一问题
Sum( arr[1...n-1 ] ) = arr[1] + Sum(arr[2...n-1]) ← 更小的问题
Sum ( arr[n-1...n-1]) = arr[n-1] + Sum([]) ← 最基本的问题
-
注意递归函数的宏观语意
-
递归函数就是一个函数,完成一个功能
// 计算 arr[l...n] 范围里的数字和
递归求和
package com.zf.dg;
public class Sum {
public static void main(String[] args) {
int arrs [] = {1,2,3,4,5};
System.out.println(sum(arrs));
}
public static int sum (int arr []){
return sum(arr,0);
}
/**
* 递归求和
* @param arr
* @param l
* @return
*/
private static int sum(int [] arr,int l){
if( l == arr.length){ // 如果索引 等于数组的长度 则返回0
return 0; //求解最基本问题
}
return arr[l] + sum(arr,l+1); // 把原问题转化成更小的问题
}
}