数据结构
链表,数组,栈,hash
前言
算法学习——数据结构
一、链表
算法的基础是数据结构,所有数据结构的基础都是“创建”+“增删改查”,所有链表的算法题分解到最后都是这些。
1.单链表的概念
单链表:每个节点都有唯一的next指针指向下一个唯一的节点(每一个节点可以被多个节点指向)。
如下两张图
因为C1节点指向了多个节点所以不为单链表
下图为单链表
注:在做题时需注意比较的是节点还是值;
2.链表的相关概念
节点:每一个节点都由“该节点的值”和“指向下一个节点的指针”而构成。
头节点:每一条链表都可通过从链表的第一个节点遍历,从而对链表进行增删改查。该节点被称为头节点(head)
虚拟节点(指针):通过虚拟节点可以对链表增删改查,操作过程中又不会改变链表结构。(由于本人操作习惯,下文中的虚拟节点被命名为temp)
3.创建链表
首先先了解一下jvm中是如何创建链表的。在jvm中有栈区和堆区。栈区中主要存放的是引用地址,堆区存放对象。
上图中 在栈中存放了一个对象引用,指向了链表的第一个节点,节点的next指针又指向了下一个节点,非常简单的便创建出了一个单链表。
链表创建代码实现如下
public class BasicLink {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
Node head = initLinkedList(a);
System.out.println(head);
}
private static Node initLinkedList(int[] array) {
Node head=null;//头节点
Node temp=null;//虚拟节点(指针)
for (int i=0;i<array.length;i++){
Node node=new Node(array[i]);
if (i==0){
head=node;
temp=node;
}else {
temp.next=node;
temp=node;
}
}
return head;
}
static class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
this.next =null;
}//节点构造器
@Override
public String toString() {
return "Node{" +
"val=" + val +
'}';
}
}
}
4.链表的增删改查
1.遍历链表(下面的代码是通过遍历链表得到链表长度)
public static int getLength(Node head) {
int length=0;
Node temp = head;
while (temp!=null){
length++;
temp=temp.next;
}
return length;
}
2.插入节点
/**
* 链表插入
*
* @param head 链表头节点
* @param nodeInsert 待插入节点
* @param position 待插入位置,取值从2开始
* @return 插入后得到的链表头节点
*/
public static Node insertNode(Node head, Node nodeInsert, int position) {
Node temp=head;
if (head==null){
return nodeInsert;
}//防止空指针异常
int size=getLength(head);
if (position>size+1||position<1){
System.out.println("位置参数越界");
return head;
}//检验位置参数
if (position==1){
nodeInsert.next=temp;
return nodeInsert;
}//插入链表头
int i=1;
while (i<position-1){
temp=temp.next;
i++;
}//定位
nodeInsert.next=temp.next;
temp.next=nodeInsert;
return head;
}
3.删除节点
删除节点有3种情况:删除头节点,删除尾节点,删除中间节点。下面三张图分别对应三种情况。
头
尾
中间
代码实现如下
/**
* 删除节点
*
* @param head 链表头节点
* @param position 删除节点位置,取值从1开始
* @return 删除后的链表头节点
*/
public static Node deleteNode(Node head, int position) {
if (head==null){
return null;
}//若为空则直接返回
int size=getLength(head);
if (position<=0||position>size){
System.out.println("输入的参数有误");
return head;
}//参数检验
if (position==1){
return head.next;
}//删除头
Node temp=head;
int i=1;
while (i<position-1){
temp=temp.next;
i++;
}//定位
temp.next=temp.next.next;//同时可满足中尾删除
return head;
}
5.练习:双向链表的创建和增删改查
和单向链表基本相同,只是每个节点增加了前向指针(pre),增删改查操作和单向链表差距不大,多了对pre的操作,这里不做过多的解释,直接看下面的代码。
public class DoubleLinkList {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
Node head = initLinkedList(a);
System.out.println(head);
}
static class Node {
final int data;
Node next;
Node pre;
public Node(int data) {
this.data = data;
this.next=null;
this.pre=null;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
}
private static Node initLinkedList(int[] array) {
Node head=null;
Node temp=null;
for (int i=0;i<array.length;i++){
Node node=new Node(array[i]);
if (i==0){
head=node;
temp=node;
}else {
temp.next=node;
node.pre=temp;
temp=node;
}
}
return head;
}
/**
* 获取链表长度
*
* @param head 链表头节点
* @return 链表长度
*/
public static int getLength(Node head) {
int length=0;
Node temp = head;
while (temp!=null){
length++;
temp=temp.next;
}
return length;
}
/**
* 链表插入
*
* @param head 链表头节点
* @param nodeInsert 待插入节点
* @param position 待插入位置,取值从2开始
* @return 插入后得到的链表头节点
*/
public static Node insertNode(Node head, Node nodeInsert, int position) {
Node temp=head;
if (head==null){
return nodeInsert;
}//防止空指针异常
int size=getLength(head);
if (position>size+1||position<1){
System.out.println("位置参数越界");
return head;
}//检验位置参数
if (position==1){
nodeInsert.next=temp;
temp.pre=nodeInsert;
return nodeInsert;
}//插入链表头
int i=1;
while (i<position-1){
temp=temp.next;
i++;
}//定位
nodeInsert.next=temp.next;
nodeInsert.pre=temp;
temp.next.pre=nodeInsert;
temp.next=nodeInsert;
return head;
}
/**
* 删除节点
*
* @param head 链表头节点
* @param position 删除节点位置,取值从1开始
* @return 删除后的链表头节点
*/
public static Node deleteNode(Node head, int position) {
if (head==null){
return null;
}
int size=getLength(head);
if (position<=0||position>size){
System.out.println("输入的参数有误");
return head;
}
if (position==1){
head.next.pre=null;
return head.next;
}
Node temp=head;
int i=1;
while (i<position-1){
temp=temp.next;
i++;
}
if (position==size){
temp.next=temp.next.next;
return head;
}
temp.next=temp.next.next;
temp.next.next.pre=temp;
return head;
}
}