设计一个链表结构来对数组进行排序,并可以添加删除节点
1. 链表结构
class MyLink {
public int data;
public MyLink next;
/* 构造函数 */
public MyLink(int data) {
super();
this.data = data;
}
/* 重载toString方法 */
@Override
public String toString() {
return "[[data="+ this.data + "]]";
}
}
包含一个构造函数、重载了toString方法,方便之后创建新的节点,以及打印节点数据。
2. 插入一个单节点
插入新节点时,从首节点开始遍历,直到遇到比当前节点大的节点为止。
private void insertMyLink(int data) {
MyLink newNode = new MyLink(data);
MyLink current = firstLink; // 定义一个当前节点,并指向首节点
MyLink previous = null; // 定义先前结点 将previous赋为null
/* 定位到需要插入的位置,previous之后 */
while((current != null) && (current.data < newNode.data)) {
previous = current;
current = current.next;
}
/* 插入节点 */
if (previous == null) { // 插在链表首部需要特殊处理
newNode.next = firstLink;
firstLink = newNode;
} else { // 包括插入两节点之间和插在链表尾部两种情况
previous.next = newNode;
newNode.next = current;
}
nodeCount++; // 插入成功之后,总节点数加一
}
但是这样实现肯定会有一个问题,那就是会重复添加节点
比如添加int n1[] = {4,5,4,1};
,就会出现下面的现象
==========START==========
[[data=1]]
[[data=4]]
[[data=4]]
[[data=5]]
==========END==========
所以,我这里做了一下判断是否重复的操作
private boolean isRepetitive(MyLink current, MyLink newNode) {
/* 这里提前判空,避免插入链表最后时出现current为null */
if (current != null && current.data == newNode.data) {
return true;
}
return false;
}
插入节点之前判断一下是否重复
/* 插入新节点 */
private void insertMyLink(int data) {
MyLink newNode = new MyLink(data);
MyLink current = firstLink; // 定义一个当前节点,并指向首节点
MyLink previous = null; // 定义先前结点 将previous赋为null
/* 定位到需要插入的位置,previous之后 */
while((current != null) && (current.data < newNode.data)) {
previous = current;
current = current.next;
}
/* 插入节点 */
if (!isRepetitive(current, newNode)) {
if (previous == null) { // 插在链表首部需要特殊处理
newNode.next = firstLink;
firstLink = newNode;
} else { // 包括插入两节点之间和插在链表尾部两种情况
previous.next = newNode;
newNode.next = current;
}
nodeCount++; // 插入成功之后,总节点数加一
}
}
加入判断之后就可以去重重复的节点。
==========START==========
[[data=1]]
[[data=4]]
[[data=5]]
==========END==========
3. 删除首节点
删除之前要进行判空,否则在获取firstLink.next
的时候可能会出现Cannot read field "next" because "this.firstLink" is null
/* 删除首节点 */
private void removeFirstLink() {
if (firstLink != null) {
firstLink = firstLink.next;
nodeCount--; // 删除成功时,总节点数减一
}
}
4. 打印链表数据
使用这种数据结构的好处就是,遍历和删除节点的时间复杂度为O(n)。
/* 打印出所有数据 */
public void printMyLink() {
System.out.println("==========START==========");
MyLink current = firstLink;
while (current != null) {
System.out.println(current);
current = current.next;
}
System.out.println("==========END==========");
}
5. 应用
给定指定的数组(numbers[]),选出两个最小的值并求出最小公倍数,将这两个数删除,并把求出的最小公倍数加入到数组中。给定一个阀值(target),当数组中所有的值都大于阀值的时候返回求最小公倍数的次数;若给定的数组剩下一个数据时,仍小于阀值,返回-1;若数组中的最小值大于阀值,返回0。
class MyLink {
public int data;
public MyLink next;
/* 构造函数 */
public MyLink(int data) {
super();
this.data = data;
}
/* 重载toString方法 */
@Override
public String toString() {
return "[[data="+ this.data + "]]";
}
}
class MyLinkSort {
private MyLink firstLink; // 首结点
private int nodeCount; // 整个链表的节点数目
public MyLinkSort() {
firstLink = null;
}
public MyLinkSort(int nums[]) {
for (int i = 0; i < nums.length; i++) {
insertMyLink(nums[i]);
}
}
/* 去除重复的节点 */
private boolean isRepetitive(MyLink current, MyLink newNode) {
/* 这里提前判空,避免插入链表最后时出现current为null */
if (current != null && current.data == newNode.data) {
return true;
}
return false;
}
/* 插入新节点 */
private void insertMyLink(int data) {
MyLink newNode = new MyLink(data);
MyLink current = firstLink; // 定义一个当前节点,并指向首节点
MyLink previous = null; // 定义先前结点 将previous赋为null
/* 定位到需要插入的位置,previous之后 */
while((current != null) && (current.data < newNode.data)) {
previous = current;
current = current.next;
}
/* 插入节点 */
if (!isRepetitive(current, newNode)) {
if (previous == null) { // 插在链表首部需要特殊处理
newNode.next = firstLink;
firstLink = newNode;
} else { // 包括插入两节点之间和插在链表尾部两种情况
previous.next = newNode;
newNode.next = current;
}
nodeCount++;
}
}
/* 删除首节点 */
private void removeFirstLink() {
if (firstLink != null) {
firstLink = firstLink.next;
nodeCount--;
}
}
/* 打印出所有数据 */
public void printMyLink() {
System.out.println("==========START==========");
MyLink current = firstLink;
while (current != null) {
//current.printLink();
System.out.println(current);
current = current.next;
}
System.out.println("==========END==========");
}
public void insertAndRemoveFirst(int data) {
removeFirstLink();
removeFirstLink();
insertMyLink(data);
}
public int getNodeCount() {
return nodeCount;
}
public int getFirstLinkNodeData() {
return firstLink.data;
}
public int getFirstLinkNextNodeData() {
return firstLink.next.data;
}
public static void main(String[] args) {
int n1[] = {4,5,4,1};
MyLinkSort myLinkSort = new MyLinkSort(n1);
myLinkSort.printMyLink();
}
}
public class T2_improve {
private boolean bIsEnd(int target, MyLinkSort myLinkSort) {
if (myLinkSort.getFirstLinkNodeData() >= target) {
return true;
}
return false;
}
private int getGCD(int a, int b) {
int getGCD = (a % b == 0) ? b : getGCD(b, a % b);
return getGCD;
}
/* 获取最大公约数 */
private int getLCM(int a, int b) {
int getLCM = (a * b) / getGCD(a, b);
return getLCM;
}
private int numOperations(int target, int[] numbers) {
MyLinkSort myLinkSort = new MyLinkSort(numbers);
return myFun(target, myLinkSort, 0);
}
public int myFun(int target, MyLinkSort myLinkSort, int count) {
if (bIsEnd(target, myLinkSort)) {
return count;
}
if (myLinkSort.getNodeCount() == 1) {
if (myLinkSort.getFirstLinkNodeData() < target) {
return -1;
}
return count;
}
/* 获取最小的两个数的最大公约数 */
int lcm = getLCM(myLinkSort.getFirstLinkNodeData(), myLinkSort.getFirstLinkNextNodeData());
myLinkSort.insertAndRemoveFirst(lcm);
count++;
return myFun(target, myLinkSort, count);
}
public static void main(String[] args) {
int n1[] = {4,5,4,1};
int n2[]={4,4,4,4,4};
int n3[]={100,300};
int n4[]={5,1,9,16,23};
T2_improve t = new T2_improve();
System.out.println(t.numOperations(3,n1));
System.out.println(t.numOperations(8,n2));
System.out.println(t.numOperations(99,n3));
System.out.println(t.numOperations(45,n4));
}
}