JAVA写一个单链表
1、已知带头结点的动态单链表 L 中的结点是按整数值递增排序的,试写一 算法将值为 x 的结点插入到表 L 中,使 L 仍然有序。要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
2、设计一算法,逆置带头结点的动态链表 L。要求利用原表的结点空间, 并要求用尽可能少的时间完成。
3、假设有两个按元素值递增有序的线性表 A 和 B,均以单链表作存储结构, 试编写算法将 A 表和 B 表归并成一个按元素值递减有序的线性表性表 C,并要求 利用原表的空间存放 C,并要求用尽可能少的时间完成。
如何插入一段漂亮的代码片
- 要求一:不破坏表的顺序插入元素,时间复杂度O(n)
一个for循环即可解决
//
public void Insert_order(int e){
Node current = header;
if(header == null) //空表直接尾部加
add(e);//在尾部添加元素
int i;
for(i=0;i<size&¤t!=null;i++,current=current.next){
if(current.data>=e){//如果某个结点的指大于等于输入值
if(i==0) {//如果是在头节点,直接加在前面
addHead_e(e);//在头部添加元素
break;
}
Node p=this.getNodeByIndex(i-1);//指定位置的前一个节点
p.next=new Node(e,p.next);//这是不在首位的情况,获取当前节点的前一个节点并将e插在它后面,同时新节点的指向是原节点的下一个节点
size++;
break;
}
}
if(i==size)//如果循环没有被break结束,那么i的值与size的值相等,
add(e);//这说明这个元素比原链表中任何一个都大,直接在尾部添加节点即可
//System.out.println(size);
}
用到的add(e)和addHead_e(e)方法:
//直接在尾部插入元素
public void add(int e){
if(header==null) {
header = new Node(e, null);
tail = header;
}
else{
Node p=new Node(e,null);
tail.next=p;
tail=p;
}
size++;
}
//在表头插入元素
public void addHead_e(int e){
Node p=new Node(e,null);
p.next=header;
header=p;
if(header.next==null)
tail=header;
size ++;
}
用到的getNodeByIndex(index)方法:
// 获取指定节点
private Node getNodeByIndex(int index){
if(index < 0 || index >= size)
throw new IndexOutOfBoundsException("索引超出线性表范围");
Node current = header;//从header开始遍历
for(int i=0; i<size && current!=null; i++,current=current.next){
if(i == index)
return current;
}
return null;
}
- 要求2:逆置带头结点的动态单链表 L
一共有三种方法,这里选择的是迭代法
事实上这里可以不需要返回值的,截至到header=p就可以了
具体结合实际应用
//逆置(迭代法)
/*
1.先将当前节点的下一节点记录下来,
2.然后将当前节点指向上一节点
3.再将当前节点记录下来,让记录的下一节点变成当前节点
* */
public Node ReverseLink1(){
if(header==null||header.next==null)//如果表头为空或者当前链表只有一个节点,
return header; //那么直接返回就可以了
Node p,q;
p=null;//p用来保存上一节点,开始时,表头没有上一节点(表头的上一节点为空)
q=header;//q用来保存当前节点,从表头开始操作
while(q!=null){//只要当前节点不为空,就要进行操作
Node r=q.next;////1.r保存当前节点的下一节点
q.next=p;//2.让当前节点指向上一节点,第一次为空,第二次开始指向上一节点
p=q;//3.让上一节点变成当前节点(把当前节点记录下来,记录进p里)
q=r;//3.让记录的下一节点变成当前节点
}
header=p;
return header;
}
- 要求三:元素值递增有序的线性表 A 和 B,以单链表作存储结构,将 A 表和 B 表归并成一个按元素值递减有序的线性表性表 C,并要求 利用原表的空间存放 C
-------------------------------------------------------------------------------------也就是
将A和B两个递增单链表合成递减单链表并要求用原来的存储空间
这里我的想法是这样的:把B表的元素一个一个拿出来,插入到A表中再逆置
/*
两个递增链表合成一个递减链表
利用前面的函数和辅助节点p,将b链表的值按大小顺序插入到a表中,
形成包含a和b所有元素的递增链表,再将链表用写好的逆置方法逆置就可以了
* */
public void Combine_order_increase(My_list b) {
Node p,q;
p=this.header;
q=b.header;
while(p!=null&&q!=null)
{
this.Insert_order(q.data);
// System.out.println("t "+this+" t");
// System.out.println("m "+b+" m");
q=q.next;
}
this.ReverseLink1();
}
-
完整代码如下:
private class Node{ private int data; private Node next; public Node() {} // 初始化全部属性的构造器 public Node(int data, Node next) { this.data = data; this.next = next; } } public Node header;// 保存头结点 private Node tail;// 保存尾节点 private int size;// 保存已含有的节点数 public void Input_size(int size){ this.size=size; } public My_list(){ header=null; tail=null; } public My_list(int e){ header=new Node(e,null); tail=header; size++; } public int length(){ return size; } // 获取指定节点
private Node getNodeByIndex(int index){ if(index < 0 || index >= size) throw new IndexOutOfBoundsException("索引超出线性表范围"); Node current = header;//从header开始遍历 for(int i=0; i<size && current!=null; i++,current=current.next){ if(i == index) return current; } return null; } // 获取指定索引处的元素 public int get(int index) { return this.getNodeByIndex(index).data; } public int locate(int e){ Node current=header; for(int i=0;i<size&¤t!=null;i++,current=current.next){ if(current.data==e) return i; } throw new IndexOutOfBoundsException("索引超出线性表范围"); } //指定位置插入元素 public void Insert(int e,int index){ if(index<0||index>=size) throw new IndexOutOfBoundsException("索引超出线性表范围"); // Node current =header; if(header == null)//空表直接尾部加 add(e); else{ if(index==0)//表头直接在头部加(已排除空表) addHead_e(e); else{ Node p=getNodeByIndex(index-1);//指定位置的前一个节点 p.next=new Node(e,p.next); } } size++; } ```//直接在尾部插入元素 public void add(int e){ if(header==null) { header = new Node(e, null); tail = header; } else{ Node p=new Node(e,null); tail.next=p; tail=p; } size++; } //在表头插入元素 public void addHead_e(int e){ Node p=new Node(e,null); p.next=header; header=p; if(header.next==null) tail=header; size ++; } public void Insert_order(int e){ Node current = header; if(header == null) {//空表直接尾部加 add(e); } int i; for(i=0;i<size&¤t!=null;i++,current=current.next){ if(current.data>=e){//如果某个结点的指大于等于输入值 if(i==0) {//如果是在头节点,直接加在前面 addHead_e(e); break; } Node p=this.getNodeByIndex(i-1);//指定位置的前一个节点 p.next=new Node(e,p.next);//这是不在首位的情况,获取当前节点的前一个节点并将e插在它后面,同时新节点的指向是原节点的下一个节点 size++; break; } } if(i==size)//如果循环没有被break结束,那么i的值与size的值相等, add(e);//这说明这个元素比原链表中任何一个都大,直接在尾部添加节点即可 //System.out.println(size); }
/* 1.先将当前节点的下一节点记录下来, 2.然后将当前节点指向上一节点 3.再将当前节点记录下来,让记录的下一节点变成当前节点 * */ public Node ReverseLink1(){ if(header==null||header.next==null)//如果表头为空或者当前链表只有一个节点,那么直接返回就可以了 return header; Node p,q; p=null;//p用来保存上一节点,开始时,表头没有上一节点(表头的上一节点为空) q=header;//q用来保存当前节点,从表头开始操作 while(q!=null){//只要当前节点不为空,就要进行操作 Node r=q.next;////1.r保存当前节点的下一节点 q.next=p;//2.让当前节点指向上一节点,第一次为空,第二次开始指向上一节点 p=q;//3.让上一节点变成当前节点(把当前节点记录下来,记录进p里) q=r;//3.让记录的下一节点变成当前节点 } header=p; return header; } ```///* 两个递增链表合成一个递减链表 利用前面的函数和辅助节点p,将b链表的值按大小顺序插入到a表中, 形成包含a和b所有元素的递增链表,再将链表用写好的逆置方法逆置就可以了 //* */ public void Combine_order_increase(My_list b) { Node p,q; p=this.header; q=b.header; while(p!=null&&q!=null) { this.Insert_order(q.data); // System.out.println("t "+this+" t"); // System.out.println("m "+b+" m"); q=q.next; } this.ReverseLink1(); } @Override//重写 public String toString() {//作输出用 if(header==null){ return null; } else{ StringBuffer sb=new StringBuffer(""); for(Node current = header; current != null; current = current.next) sb.append(current.data).append(" "); int len = sb.length(); return sb.toString(); } } }
主程序入口:
public class Test {
public static void main(String[] args) throws IOException {
My_list a = new My_list();
My_list b = new My_list();
int t=1;
int size_a;
int size_b;
while (t!=0){
System.out.println("Please choose the function");
System.out.println("1.initialize the “a” linklist and the “b” linklist");
System.out.println("2.insert one data into “a” list");
System.out.println("3.reverse the a list ");
System.out.println("4.Combine the a and b list");
System.out.println("5.Output the a and b list");
t=KeyInput.readInt();
switch (t){
case 1:
System.out.println("Please input the size of a list");
size_a=KeyInput.readInt();
// a.Input_size(size_a);
System.out.println("Please input the size of b list");
size_b=KeyInput.readInt();
// b.Input_size(size_b);
System.out.println("Please input the data of a list");
for(int i=0;i<size_a;i++) {
int e=KeyInput.readInt();
a.add(e);
}
System.out.println("Please input the data of b list");
for(int i=0;i<size_b;i++) {
int e = KeyInput.readInt();
b.add(e);
}
break;
case 2:
int e=KeyInput.readInt();
a.Insert_order(e);
break;
case 3:a.ReverseLink1();
break;
case 4:a.Combine_order_increase(b);
break;
case 5:
System.out.println(a);
System.out.println(b);
break;
case 0:
System.out.println("Welcome to Use Again!");
break;
default:
System.out.println("No such a funcation was found");
System.out.println("Press any Key to Continue ");
System.in.read( );
break;
}
}
}
}
代码参考了其他人的想法,事实上是一个修改和添加功能使其适应题目,参考的链接:
链接:自己动手写一个单链表.
链接:链表的逆置(迭代法与逆置法).