数组和链表都是线性存储结构的基础,栈和队列都是线性存储结构的应用~
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的引用链
接次序实现的
组成:
由n个节点离散的分配,每个节点由头结点和尾节点组成,头结点存放date,尾节点存放下个头结点的引用,在最后的节点没有下一个节点nulll
无头定义
//不带头的
private Node head;
class Node{
private int data;
private Node next;
public Node(int data) {
this.data=data;
this.next=null;
}
}
无头的链表在new的时候不含头,在空间存储上也没有。
01单向链表的接口
public interface ILinked {
//头插法
void addFirst(int data);
//尾插法
void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
boolean addindex(int index,int data);
//查找是否包含关键字key是否在单链表当中
boolean contains(int key);
//删除第一次出现关键字为key的节点
int remove(int key);
//删除所有值为key的节点
void removeAllKey(int key);
//得到单链表的长度
int getLength();
void display();
void clear();
}
实现单链表
01addFirst【头插法】
//无头
@Override
public void addFirst(int data) {
// TODO Auto-generated method stub
//第一次插入数据
Node node = new Node(data);
if(this.head==null) {
this.head=node;
}else {
node.next=this.head;
this.head=node;
}
}
考虑到没有头结点所以在插入式第一次插入,链表为空,此时插入的数据唯一特殊情况,要考虑。
定义一个新的节点 node 对node进行判定
当进行头部插入时,如果当前链表的头结点 head为空 将这个新的节点定义为头结点,假如不为null 将新建节点的next指向之前头结点的head
02尾插法
//无头
@Override
public void addLast(int data) {
// TODO Auto-generated method stub
Node node = new Node(data);
Node cur = this.head;
if(cur==null) {
this.head=node;
}else {
while(cur.next!=null) {
cur=cur.next;
}
cur.next=node;
}
}
在操作时考虑到链表可能为null
创建一个新的节点node 对所有节点进行遍历找出尾节点然后将尾节点指向新节点
03addindex【添加指定位置的节点】
01核对下标是否合法
下标的取值范围
//无头与带头相同
private void checkIndex(int index) {
if(index<0||index>getLength()) {
throw new IndexOutOfBoundsException("下标不合法");
}
}
02寻找index-1 未知的节点
//无头寻找index前一个节点
private Node serchIndex(int index) {
checkIndex(index);
//记录从头到尾的步数
int count=0;
if(index==0){
return null;
}
Node cur = this.head;
while(cur.next!=null&&count<index-1){
cur=cur.next;
count++;
}
return cur;
}
或者
private Node searchPre(int key){
//判断是不是第一个节点
Node cur = this.head; //创建头结点
if(head.data==key){ //如果头结点符合情况 返回头结点
return cur;
}
//找到符合key节点的前一个节点
while(cur.next!=null){ //将整个链表从头到尾遍历 结束是最后一个next=null
if(cur.next.data==key){
return cur;
}else{
cur=cur.next; //不符合情况则下一个
}
}
return null;
}
通过遍历头结点到index-1的每经过依次next 计数器加一cur.next!=null&&count<index-1
//无头
@Override
public boolean addindex(int index, int data) {
// TODO Auto-generated method stub
Node node = new Node(data);
Node cur = serchIndex(index);
if(cur==null){
node.next=this.head;
this.head=node;
}else{
node.next=cur.next;
cur.next=node;
}
return true;
}
因为本身链表无头所以不能直接将index拿来做便利条件,必须判断index合法情况,在此基础上使用serachIndex函数去找前一个位置
思路 :
1.通过serchIndex找出index-1上一个节点的next节点
2.然后打断index-1和index节点的连接现将node的next节点指向index的头结点,然后将index-1的next指向node的头节点
03判断是否有元素
//判断是否有元素
@Override
public boolean contains(int key) {
// TODO Auto-generated method stub
Node node = this.head;
while(node!=null){
if(node.data==key){
return true;
}else{
node=node.next;
}
}
return false;
}
通过反复遍历每一个节点通过对比寻找元素
04删除节点
@Override
public int remove(int key) {
// TODO Auto-generated method stub
int oldData = 0;
Node pre = searchPre(key); //找到符合情况的前一个节点
if(pre==null){
throw new UnsupportedOperationException("无节点");
}
if(pre==this.head&&pre.data==key){ //排除当目标节点为第二个节点时 前一节点为head
oldData=this.head.data; //拿到数据存放
this.head=this.head.next;
return oldData;
}
Node delNode=pre.next; //拿到前一个节点next节点
oldData = delNode.data;
pre.next = delNode.next; //与目标节点下一个节点相连接
return oldData;
}
05removeAllKey
@Override
public void removeAllKey(int key) {
// TODO Auto-generated method stub
Node cur = this.head.next; //pre cur curNdext cur
Node pre = this.head;
while(cur!=null){
if(cur.data==key){
pre.next=cur.next;
cur=pre.next;
}else{
pre=cur;
cur=pre.next;
}
}
if(this.head.data==key){
this.head=this.head.next;
}
}
总结:
1.单向不循环链表在执行头插法或者尾插时要考虑头结点因素
2.画图更能掌握单向链表
**总代码
public class Linkedimpl implements ILinked {
private Node head;
class Node{
private int data;
private Node next;
public Node(int data) {
this.data=data;
this.next=null;
}
}
@Override
public void addFirst(int data) {
// TODO Auto-generated method stub
//第一次插入数据
Node node = new Node(data);
if(this.head==null) {
this.head=node;
}else {
node.next=this.head;
this.head=node;
}
}
@Override
public void addLast(int data) {
// TODO Auto-generated method stub
Node node = new Node(data);
Node cur = this.head;
if(cur==null) {
this.head=node;
}else {
while(cur.next!=null) {
cur=cur.next;
}
cur.next=node;
}
}
//寻找index -1 位置的节点
private Node serchIndex(int index) {
checkIndex(index);
//记录从头到尾的步数
int count=0;
if(index==0){
return null;
}
Node cur = this.head;
while(cur.next!=null&&count<index-1){
cur=cur.next;
count++;
}
return cur;
}
private void checkIndex(int index) {
if(index<0||index>getLength()) {
throw new IndexOutOfBoundsException("下标不合法");
}
}
@Override
public boolean addindex(int index, int data) {
// TODO Auto-generated method stub
Node node = new Node(data);
Node cur = serchIndex(index);
if(cur==null){
node.next=this.head;
this.head=node;
}else{
node.next=cur.next;
cur.next=node;
}
return true;
}
//判断是否有元素
@Override
public boolean contains(int key) {
// TODO Auto-generated method stub
Node node = this.head;
while(node!=null){
if(node.data==key){
return true;
}else{
node=node.next;
}
}
return false;
}
private Node searchPre(int key){
//判断是不是第一个节点
Node cur = this.head; //创建头结点
if(head.data==key){ //如果头结点符合情况 返回头结点
return cur;
}
//找到符合key节点的前一个节点
while(cur.next!=null){ //将整个链表从头到尾遍历 结束是最后一个next=null
if(cur.next.data==key){
return cur;
}else{
cur=cur.next; //不符合情况则下一个
}
}
return null;
}
@Override
public int remove(int key) {
// TODO Auto-generated method stub
int oldData = 0;
Node pre = searchPre(key); //找到符合情况的前一个节点
if(pre==null){
throw new UnsupportedOperationException("无节点");
}
if(pre==this.head&&pre.data==key){ //排除当目标节点为第二个节点时 前一节点为head
oldData=this.head.data; //拿到数据存放
this.head=this.head.next;
return oldData;
}
Node delNode=pre.next; //拿到前一个节点next节点
oldData = delNode.data;
pre.next = delNode.next; //与目标节点下一个节点相连接
return oldData;
}
@Override
public void removeAllKey(int key) {
// TODO Auto-generated method stub
Node cur = this.head.next; //pre cur curNdext cur
Node pre = this.head;
while(cur!=null){
if(cur.data==key){
pre.next=cur.next;
cur=pre.next;
}else{
pre=cur;
cur=pre.next;
}
}
if(this.head.data==key){
this.head=this.head.next;
}
}
@Override
public int getLength() {
// TODO Auto-generated method stub
int len=0;
Node node = this.head;
while(node!=null) {
len++;
node=node.next;
}
return len;
}
@Override
public void display() {
// TODO Auto-generated method stub
Node node = this.head;
if(node==null){
System.out.println("wudi");
}
while(node!=null) {
System.out.print(node.data+" ");
node=node.next;
}
}
@Override
public void clear() {
// TODO Auto-generated method stub
while(this.head.next!=null){
Node cur = this.head.next;
this.head= null;
this.head = cur;
}
}
在执行clear操作时,要保证没一个节点得到释放
单向带头循环链表
public class CLinkedimpl implements ICLinked {
class Node{
private Node next;
private int data;
public Node(){
this.data=-1;
}
public Node(int data){
this.data=data;
}
}
private Node head;
public CLinkedimpl(){
this.head=new Node();
this.head.next=this.head;
}
@Override
public void addFirst(int data) {
Node node = new Node(data);
node.next = this.head.next;
this.head.next=node;
}
@Override
public void addLast(int data) {
Node node = new Node(data);
Node cur = this.head;
while(cur.next!=this.head){
cur = cur.next;
}
node.next=this.head;
cur.next=node;
}
private void checkIndex(int index){
if(index<0||index>getLength()){
throw new IndexOutOfBoundsException("下标不合法");
}
}
@Override
public boolean addIndex(int index, int data) {
Node node = new Node(data);
Node cur = this.head;
checkIndex(index);
for(int i=0;i<index;i++){
cur = cur.next;
}
node.next = cur.next;
cur.next=node;
return true;
}
@Override
public boolean contains(int key) {
Node cur = this.head;
while(cur.next!=this.head){
if(cur.next.data==key){
return true;
}else{
cur=cur.next;
}
}
return false;
}
private Node searchPre(int key){
Node pre = this.head;
while(pre.next!=this.head){
if(pre.next.data==key){
return pre;
}else{
pre=pre.next;
}
}
return null;
}
@Override
public int remove(int key) {
int oldData=0;
Node pre = searchPre(key);
if(pre==null){
System.out.print("无该节点");
}
oldData = pre.next.data;
pre.next=pre.next.next;
/**
* Node delNode = pre.next;
* oldData = delNode.data;
* pre.next = delNdoe.next;
*/
return oldData;
}
@Override
public void removeAllKey(int key) {
int oldData = 0;
Node pre = this.head;
Node cur = this.head.next;
while(cur!=this.head){
if(cur.data==key){
pre.next=cur.next;
cur=cur.next;
}else{
cur=pre;
cur=cur.next;
}
}
}
@Override
public int getLength() {
Node node = this.head.next;
int count = 0;
while(node!=this.head){
count++;
}
return count;
}
@Override
public void display() {
Node node = this.head.next;
while(node!=this.head){
System.out.print(node.data+" ");
node=node.next;
}
System.out.println();
}
@Override
public void clear() {
while(this.head.next!=this.head){
Node cur = this.head.next;
this.head.next=cur.next;
cur.next=null;
}
}
}
带头循环的优点,相比于无头节点,在执行头插法或者尾插法不用考虑头结点。执行遍历时终止条件是转一个圈的头结点
生命不息!!!奋斗不止!!!