题目:利用单链表求集合的交集、并集和补集
要求:使用泛型
/**
* 利用单链表求集合的交集、并集和补集
* 要求:使用泛型
*
* @author zql
*
*/
public class SingleLinkedList<T> extends Object{
/**
* 定义链表个数
*/
private int size;
/**
* 头节点
*/
private Node<T> head;
// 构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行
{
this.size = 0;
this.head = null;
}
/**
* 定义一个无参构造函数,用于实例化头节点
*/
public SingleLinkedList() {
this.head = new Node<T>();
}
/**
* 定义一个添加集合元素到链表节点的构造函数
*
* @param values
*/
public SingleLinkedList(T[] values) {
this();
this.set(values);
}
/**
* 链表的每个节点类
*
* @param <T>
*/
public class Node<E> {
/**
* 每个节点的数据
*/
private T data;
/**
* 每个节点指向下一个节点的连接
*/
public Node<E> next;
public Node() {
this(null);
}
public Node(T data) {
this.data = data;
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
}
/**
* 设置每一个节点的数据
*
* @param valuse
*/
public void set(T[] values) {
if (values == null || values.length == 0) {
throw new NullPointerException(this.getClass().getName() + "构造参数长度不能为0且不能为空");
} else {
Node<T> rear = this.head;
// 给头节点设置第一个元素
rear.setData(values[0]);
// 节点数加一
this.size++;
for (int i = 1, len = values.length; i < len; i++) {
// 数据为null不添加入节点
if (values[i] == null) {
continue;
}
rear.next = new Node<T>(values[i]);
rear = rear.next;
// 节点数加一
this.size++;
}
}
}
/**
* 在链表头添加元素
*
* @param n
* @return
*/
public void add(T value) {
if (value == null) {
throw new NullPointerException("添加的元素不能为空");
}
Node<T> newNode = new Node<T>(value);
// 如果链表节点数为0,则头节点就是添加的新节点
if (this.size == 0) {
this.head = newNode;
} else {
// next指向原头节点
newNode.next = this.head;
// 将当前添加的节点设置为头节点
this.head = newNode;
}
// 链表节点数加一
this.size++;
}
/**
* 在链表头指定位置添加元素
*
* @param i 从0开始,0表示添加进第一位,如果i大于等于size则添加进末尾节点
* @param n
*/
public void add(int i,T value) {
Node<T> newNode = new Node<T>(value);
if (value == null) {
throw new NullPointerException("添加的元素不能为空");
}
if (i == 0) {
add(value);
} else {
Node<T> front = this.head;
// j<=i,只需查找到要插入的位置即可
for (int j = 1; j <= i && j < this.size; j++) {
// 找到要添加的位置
if (j == i) {
// 保存原链表添加位置的节点数据
Node<T> temp = front.next;
// 把新添加的节点接上
front.next = newNode;
// 把保存的节点数据连上
front.next.next = temp;
} else {
front = front.next;
}
}
// 插入最后一个位置
if (i >= this.size) {
front.next = newNode;
}
// 链表节点数加一
this.size++;
}
}
/**
* 根据索引获取节点数据
*
* @param i 索引,从0开始
* @return
*/
public T get(int i) {
if (i >= this.size || i < 0) {
throw new ArrayIndexOutOfBoundsException("数组越界:" + i);
}
if (i == 0) {
return this.head.data;
}
Node<T> current = this.head;
for (int j = 1; j < this.size; j++) {
if (i == j) {
return current.next.data;
}
current = current.next;
}
return null;
}
/**
* 根据传入的值判断节点是否存在
*
* @param value
* @return 存在返回true
*/
public boolean isExist(T value) {
Node<T> current = this.head;
while (current.next != null) {
if (value.equals(current.getData())) {
return true;
} else {
current = current.next;
}
}
return false;
}
/**
* 求两个集合的并集
*
* @param values
*/
public void merge(T[] values) {
for (int i = 0, len = values.length; i < len; i++) {
// 不存在就添加
if (!this.isExist(values[i])) {
this.add(values[i]);
}
}
}
/**
* 求两个集合的交集
*
* @param values
*/
@SuppressWarnings("unchecked")
public void intersection(T[] values,SingleLinkedList<T> s) {
// 也可以使用list,我想复习一个数组扩容就没用
// 泛型无法实例化,所以使用实例化Object强制转换方式
T[] tArr = (T[]) new Object[10];
int j = 0;
for (int i = 0, len = values.length; i < len; i++) {
// 存在就添加
if (this.isExist(values[i])) {
tArr[j++] = values[i];
// 进行评定是否达到,数组的最大存储量
if (j >= tArr.length) {
// 扩容
tArr = getNewArray(tArr);
}
}
}
// 清空数据
s.clear();
// 重新装数据
s.set(tArr);
}
/**
* 数组的自动扩容,同时拷贝数据
*
* @param arrs 原始的数组
* @return T [] 新的数组对象
* @说明: 本方法扩容的因子为 50%
*/
@SuppressWarnings("unchecked")
private T[] getNewArray(T[] arrs) {
int maxnum = (int) (arrs.length * 1.5);
T[] newArr = (T[]) new Object[maxnum];
// 拷贝数据
int i = 0;
// 迭代器 保证每个元素都会被迭代一次
for (T x : arrs) {
newArr[i++] = x;
}
return newArr;
}
/**
* 移除指定位置节点数据
*
* @param i
*/
public void remove(int i) {
if (i >= this.size || i < 0) {
throw new ArrayIndexOutOfBoundsException("数组越界:" + i);
}
// 如果移除的节点是第一个节点
if (i == 0) {
this.head = this.head.next;
} else {
Node<T> front = this.head;
for (int j = 1; j < i && front != null; j++) {
front = front.next;
}
if (front.next != null) {
front.next = front.next.next;
}
}
// 节点数减一
this.size--;
}
/**
* 根据传入的值移除所有和该值相同的节点
*
* @param values
*/
public void remove(T value) {
if (value == null) {
throw new NullPointerException("移除的元素不能为空");
}
Node<T> front = this.head;
// 要移除的位置
int i = 0;
// 是否触发过移除操作
boolean isRemove = false;
while (front != null) {
if (front.getData().equals(value)) {
this.remove(i);
// 重置i
i = 0;
isRemove = true;
}
// 如果触发过移除操作,则重新赋予头节点,同时把是否触发过移除操作设为false
if (isRemove) {
front = this.head;
isRemove = false;
} else {
i++;
front = front.next;
}
}
}
/**
* 根据传入的数组移除所有和该数组元素值相同的节点
*
* @param values
*/
public void removeAll(T[] values) {
if (values != null && values.length != 0) {
for (int i = 0, len = values.length; i < len; i++) {
this.remove(values[i]);
}
}
}
/**
* 去掉链表中重复的值
*/
@SuppressWarnings("unchecked")
public void removeDuplicate() {
T[] tArr = (T[]) new Object[this.size];
int l = 0;
Node<T> current = this.head;
for (int i = 0; i < this.size; i++) {
tArr[l++] = current.getData();
current = current.next;
}
// 对数组去重,可以使用map或set集合,会更加简便
for (int i = 0, len = tArr.length; i < len; i++) {
// 等于null说明是重复的赋值为null就不需要再循循环了
if (tArr[i] != null) {
for (int j = i; j < len - 1; j++) {
// 这里tArr[j+1]!=null同样是因为重复的已为null
if (tArr[i] == tArr[j + 1] && tArr[j + 1] != null) {
tArr[j + 1] = null;
}
}
}
}
// 清空数据
this.clear();
// 重新装数据
this.set(tArr);
}
/**
* 去泛型数组中重复的值
*
* @param tArr 要去重的泛型数组
* @return 返回去重后的Object类型数组
* <br/>不返回泛型数组是因为返回了也接收不了,因为java中的强制类型转换只是针对单个对象的,将整个数组转换成另外一种类型的数组是不行的
*/
public Object[] removeDuplicate(T[] tArr) {
// 去掉的重复个数
int count = 0;
// 对数组去重,可以使用map或set集合,会更加简便
for (int i = 0, len = tArr.length; i < len; i++) {
// 等于null说明是重复的赋值为null就不需要再循循环了
if (tArr[i] != null) {
for (int j = i; j < len - 1; j++) {
// 这里tArr[j+1]!=null同样是因为重复的已为null
if (tArr[i] == tArr[j + 1] && tArr[j + 1] != null) {
tArr[j + 1] = null;
count++;
}
}
}
}
if (count == 0) {
return tArr;
}
// 拷贝去重后的数组
Object[] newArr = new Object[tArr.length - count];
int l = 0;
for (int i = 0, len = tArr.length; i < len; i++) {
if (tArr[i] != null) {
newArr[l++] = tArr[i];
}
}
return newArr;
}
/**
* 清空所有数据
*
* @param i
*/
public void clear() {
this.head = new Node<T>();
this.size = 0;
}
/**
* 返回链表大小
*
* @return
*/
public int size() {
return this.size;
}
/**
* 判断链表是否为空
*
* @return
*/
public boolean isEmpty(){
return (this.size == 0);
}
/**
* 打印节点信息
*
* @return
*/
public String display() {
String str = "{";
if (this.head != null && this.size != 0) {
str += this.head.getData() + ",";
for (Node<T> p = this.head.next; p != null; p = p.next) {
str += p.getData() + ",";
}
str = str.substring(0, str.length() - 1);
}
return str + "}";
}
}
测试类:
public class Test {
public static void main(String[] args) {
System.out.println("----------求补集---------");
complementarySet();
System.out.println("----------求并集---------");
unionSet();
System.out.println("----------求交集---------");
intersection();
}
/**
* 求补集
*/
public static void complementarySet() {
String[] c1 = { "b", "c", "d", "e", "a" };
String[] c2 = { "b", "c", "f", "g" };
SingleLinkedList<String> s1 = new SingleLinkedList<String>(c1);
SingleLinkedList<String> s2 = new SingleLinkedList<String>(c2);
System.out.println("初始数据:" + s1.display());
s1.add("a");
System.out.println("添加头节点后的数据:" + s1.display());
s1.add(5, "f");
System.out.println("添加头节点后再在第6位插入后数据:" + s1.display());
s1.removeDuplicate();
System.out.println("去重后的数据:" + s1.display());
// 补集:属于全集U不属于集合A的元素组成的集合称为集合A的补集
System.out.print("全集U:" + s1.display());
s1.removeAll(c2);
System.out.println(",集合A:" + s2.display() + ",属于全集U不属于集合A的补集:" + s1.display());
s1.clear();
System.out.println("清空后:" + s1.display());
}
/**
* 求并集
*/
public static void unionSet() {
String[] c1 = { "a", "b", "c", "d", "e", "a" };
String[] c2 = { "b", "c", "f", "g" };
SingleLinkedList<String> s2 = new SingleLinkedList<String>(c2);
Object[] o1 = s2.removeDuplicate(c1);// 对数组进行去重
String[] newC = new String[o1.length];
for (int i = 0, len = o1.length; i < len; i++) {
newC[i] = (String) o1[i];
}
// 其实也可以直接使c1,因为重复的元素已经变成null了
SingleLinkedList<String> s1 = new SingleLinkedList<String>(newC);
System.out.println("初始数据:" + s1.display());
s1.add(5, "f");
System.out.println("在第6位插入后数据:" + s1.display());
// 补集:属于全集U不属于集合A的元素组成的集合称为集合A的补集
System.out.print("集合A:" + s1.display());
s1.merge(c2);
System.out.println(",集合B:" + s2.display() + ",集合A和集合B的并集:" + s1.display());
}
/**
* 求交集
*/
public static void intersection() {
String[] c1 = { "b", "c", "d", "e", "a" };
String[] c2 = { "b", "c", "f", "g" };
SingleLinkedList<String> s1 = new SingleLinkedList<String>(c1);
SingleLinkedList<String> s2 = new SingleLinkedList<String>(c2);
System.out.println("初始数据:" + s1.display());
s1.add("a");
System.out.println("添加头节点后的数据:" + s1.display());
s1.add(5, "f");
System.out.println("添加头节点后再在第6位插入后数据:" + s1.display());
// 补集:属于全集U不属于集合A的元素组成的集合称为集合A的补集
System.out.print("集合A:" + s1.display());
s1.intersection(c2, s1);
System.out.println(",集合B:" + s2.display() + ",集合A和集合B的交集:" + s1.display());
}
}
测试效果图: