java算法编程题:利用单链表求集合的交集、并集和补集

题目:利用单链表求集合的交集、并集和补集
要求:使用泛型

/**
 * 利用单链表求集合的交集、并集和补集
 * 要求:使用泛型
 * 
 * @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());
	}
}

测试效果图:
在这里插入图片描述

发布了55 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/mr_zql/article/details/101642387